go-godo / godo

golang build tool in the spirt of rake, gulp
MIT License
536 stars 31 forks source link

fswatch is not concurrency-safe #55

Open pdf opened 7 years ago

pdf commented 7 years ago

The upstream project and the godo version both share the problem of concurrent map access:

fatal error: concurrent map read and map write

goroutine 15 [running]:
runtime.throw(0x7a90b5, 0x21)
        /usr/lib/go/src/runtime/panic.go:596 +0x95 fp=0xc420059968 sp=0xc420059948
runtime.mapaccess2_faststr(0x73f800, 0xc4201aa330, 0xc4203f7a80, 0x72, 0xc4201904e0, 0x300000002)
        /usr/lib/go/src/runtime/hashmap_fast.go:326 +0x50a fp=0xc4200599c8 sp=0xc420059968
github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/fswatch.getWalker.func1(0xc4203f7a80, 0x72, 0x9250a0, 0xc4203d9520, 0x0, 0x0, 0x0, 0x0)
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/fswatch/watcher.go:198 +0xf3 fp=0xc420059a48 sp=0xc4200599c8
path/filepath.walk(0xc4203f7a80, 0x72, 0x9250a0, 0xc4203d9520, 0xc4205ae090, 0x0, 0x0)
        /usr/lib/go/src/path/filepath/path.go:351 +0x81 fp=0xc420059b20 sp=0xc420059a48
path/filepath.walk(0xc4202bca80, 0x63, 0x9250a0, 0xc4203d88f0, 0xc4205ae090, 0x0, 0x0)
        /usr/lib/go/src/path/filepath/path.go:376 +0x414 fp=0xc420059bf8 sp=0xc420059b20
path/filepath.walk(0xc4206db260, 0x5b, 0x9250a0, 0xc4203d8750, 0xc4205ae090, 0x0, 0x0)
        /usr/lib/go/src/path/filepath/path.go:376 +0x414 fp=0xc420059cd0 sp=0xc420059bf8
path/filepath.walk(0xc42025bc20, 0x50, 0x9250a0, 0xc4203d85b0, 0xc4205ae090, 0x0, 0x0)
        /usr/lib/go/src/path/filepath/path.go:376 +0x414 fp=0xc420059da8 sp=0xc420059cd0
path/filepath.walk(0xc42025bae0, 0x45, 0x9250a0, 0xc4203d8410, 0xc4205ae090, 0x0, 0x0)
        /usr/lib/go/src/path/filepath/path.go:376 +0x414 fp=0xc420059e80 sp=0xc420059da8
path/filepath.walk(0xc4201a3c40, 0x3e, 0x9250a0, 0xc4207f1c70, 0xc4205ae090, 0x0, 0x17)
        /usr/lib/go/src/path/filepath/path.go:376 +0x414 fp=0xc420059f58 sp=0xc420059e80
path/filepath.Walk(0xc4201a3c40, 0x3e, 0xc4205ae090, 0x0, 0x0)
        /usr/lib/go/src/path/filepath/path.go:398 +0x14c fp=0xc420059fb8 sp=0xc420059f58
runtime.goexit()
        /usr/lib/go/src/runtime/asm_amd64.s:2197 +0x1 fp=0xc420059fc0 sp=0xc420059fb8
created by github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/fswatch.(*Watcher).addPaths
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/fswatch/watcher.go:162 +0x82

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x96de4c)
        /usr/lib/go/src/runtime/sema.go:47 +0x34
sync.(*WaitGroup).Wait(0x96de40)
        /usr/lib/go/src/sync/waitgroup.go:131 +0x7a
github.com/example/project/Gododir/vendor/gopkg.in/godo%2ev2.godoExit(0x7b1e08, 0x0, 0x0, 0x0, 0x7b2100)
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/runner.go:183 +0x760
github.com/example/project/Gododir/vendor/gopkg.in/godo%2ev2.godo(0x7b1e08, 0x0, 0x0, 0x0)
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/runner.go:93 +0x55
github.com/example/project/Gododir/vendor/gopkg.in/godo%2ev2.Godo(0x7b1e08)
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/runner.go:89 +0x46
main.main()
        /storage/workspace/golang/src/github.com/example/project/Gododir/main.go:127 +0x16b

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
        /usr/lib/go/src/runtime/asm_amd64.s:2197 +0x1

goroutine 5 [syscall]:
os/signal.signal_recv(0x0)
        /usr/lib/go/src/runtime/sigqueue.go:116 +0x104
os/signal.loop()
        /usr/lib/go/src/os/signal/signal_unix.go:22 +0x22
created by os/signal.init.1
        /usr/lib/go/src/os/signal/signal_unix.go:28 +0x41

goroutine 11 [chan receive]:
github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher.(*Watcher).eventLoop(0xc4201d8b90)
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/watcher.go:84 +0x7e
created by github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher.(*Watcher).Start
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/watcher.go:188 +0x3f

goroutine 12 [chan receive]:
github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/fswatch.(*Watcher).watchItemListener(0xc4201aa300)
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/fswatch/watcher.go:170 +0x72
created by github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/fswatch.(*Watcher).Start
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/fswatch/watcher.go:72 +0x11f

goroutine 102 [select]:
github.com/example/project/Gododir/vendor/gopkg.in/godo%2ev2.(*Project).watchTask(0xc420021130, 0xc4200dfcc0, 0x799d1d, 0x1, 0x79cd9b, 0x7, 0xc420057f70)
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/project.go:398 +0x4e0
github.com/example/project/Gododir/vendor/gopkg.in/godo%2ev2.(*Project).Watch.func1.1.1(0xc420021130, 0xc4200dfcc0, 0x79cd9b, 0x7, 0x79cd9b, 0x7, 0x799d1d, 0x1)
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/project.go:529 +0xb1
created by github.com/example/project/Gododir/vendor/gopkg.in/godo%2ev2.(*Project).Watch.func1.1
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/project.go:530 +0xe1

goroutine 13 [chan receive]:
github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/fswatch.(*Watcher).watch(0xc4201aa300, 0xc4201e27e0)
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/fswatch/watcher.go:128 +0x82
created by github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/fswatch.(*Watcher).Start
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/watcher/fswatch/watcher.go:75 +0x9d

goroutine 114 [syscall]:
syscall.Syscall6(0xf7, 0x1, 0x61df, 0xc420206d30, 0x1000004, 0x0, 0x0, 0x0, 0x60, 0xc4201f4780)
        /usr/lib/go/src/syscall/asm_linux_amd64.s:44 +0x5
os.(*Process).blockUntilWaitable(0xc42031d8c0, 0xc42043ea90, 0x0, 0x1)
        /usr/lib/go/src/os/wait_waitid.go:28 +0xa5
os.(*Process).wait(0xc42031d8c0, 0xc420206f18, 0x409ede, 0x723fa0)
        /usr/lib/go/src/os/exec_unix.go:22 +0x4d
os.(*Process).Wait(0xc42031d8c0, 0xe100000000000000, 0x8, 0xe11e91606aa82427)
        /usr/lib/go/src/os/exec.go:115 +0x2b
os/exec.(*Cmd).Wait(0xc42008e840, 0xc420116a20, 0xc420206f80)
        /usr/lib/go/src/os/exec/exec.go:435 +0x62
github.com/example/project/Gododir/vendor/gopkg.in/godo%2ev2.(*command).runAsync.func1(0xc420208560, 0xc42008e840, 0x79d129, 0x7)
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/cmd.go:113 +0xf6
created by github.com/example/project/Gododir/vendor/gopkg.in/godo%2ev2.(*command).runAsync
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/cmd.go:115 +0x11f

goroutine 104 [chan receive]:
github.com/example/project/Gododir/vendor/gopkg.in/godo%2ev2.godoExit.func1(0xc42015c060, 0xc420021130)
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/runner.go:173 +0x68
created by github.com/example/project/Gododir/vendor/gopkg.in/godo%2ev2.godoExit
        /storage/workspace/golang/src/github.com/example/project/Gododir/vendor/gopkg.in/godo.v2/runner.go:181 +0x74e

goroutine 103 [select, locked to thread]:
runtime.gopark(0x7b2510, 0x0, 0x79c0df, 0x6, 0x18, 0x2)
        /usr/lib/go/src/runtime/proc.go:271 +0x13a
runtime.selectgoImpl(0xc420256f50, 0x0, 0x18)
        /usr/lib/go/src/runtime/select.go:423 +0x1364
runtime.selectgo(0xc420256f50)
        /usr/lib/go/src/runtime/select.go:238 +0x1c
runtime.ensureSigM.func1()
        /usr/lib/go/src/runtime/signal_unix.go:434 +0x2dd
runtime.goexit()
        /usr/lib/go/src/runtime/asm_amd64.s:2197 +0x1

Whenever a map is used with concurrency, a mutex or similar must be used to avoid concurrent access.