Skip to content

Commit

Permalink
fuse: fix deadlock in TestCacheControl, update docs
Browse files Browse the repository at this point in the history
This test mmap()s a file, which means reading the
memory can block, triggering file I/O and FUSE requests.
With GOMAXPROCS=1, we don't have a free thread to handle
those requests, deadlocking the test:

	GOMAXPROCS=1 go test -v -count=1 -run TestCacheControl ./fuse/test

Use mlock() to fix that, also add a warning to the
"fuse" package docs.

Fixes hanwen#261

Change-Id: Ia32fb1826c2be602cf4f0f6cf73956af797c940c
  • Loading branch information
rfjakob authored and hanwen committed Jan 25, 2021
1 parent e29d310 commit 8e0bbdb
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 1 deletion.
6 changes: 6 additions & 0 deletions fuse/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@
// Typically, each call of the API happens in its own
// goroutine, so take care to make the file system thread-safe.
//
// Be careful when you access the FUSE mount from the same process. An access can
// tie up two OS threads (one on the request side and one on the FUSE server side).
// This can deadlock if there is no free thread to handle the FUSE server side.
// Run your program with GOMAXPROCS=1 to make the problem easier to reproduce,
// see https://github.com/hanwen/go-fuse/issues/261 for an example of that
// problem.
//
// Higher level interfaces
//
Expand Down
10 changes: 9 additions & 1 deletion fuse/test/cachecontrol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,19 @@ func TestCacheControl(t *testing.T) {
defer func() {
xmunmap(fmmap)
}()
xmlock(fmmap)

// assertMmapRead asserts that file's mmaped memory reads as dataOK.
assertMmapRead := func(subj, dataOK string) {
t.Helper()
// Use the Mlock() syscall to get the mmap'ed range into the kernel
// cache again, triggering FUSE reads as neccessary. A blocked syscall does
// not count towards GOMAXPROCS, so there should be a thread available
// to handle the FUSE reads.
// If we don't Mlock() first, the memory comparison triggers a page
// fault, which blocks the thread, and deadlocks the test reliably at
// GOMAXPROCS=1.
// Fixes https://github.com/hanwen/go-fuse/issues/261 .
xmlock(fmmap)
if string(fmmap) != dataOK {
t.Fatalf("%s: file mmap: got %q ; want %q", subj, fmmap, dataOK)
}
Expand Down

0 comments on commit 8e0bbdb

Please sign in to comment.