// Close removes all watches and closes the events channel.
func (w *Watcher) Close() error {
+ w.mu.Lock()
if w.isClosed() {
+ w.mu.Unlock()
return nil
}
// Send 'close' signal to goroutine, and set the Watcher to closed.
close(w.done)
+ w.mu.Unlock()
// Wake up goroutine
w.poller.wake()
_ = <-w.Events // consume Remove event
<-time.After(50 * time.Millisecond) // wait IN_IGNORE propagated
- w.mu.Lock()
- defer w.mu.Unlock()
- if len(w.watches) != 0 {
- t.Fatalf("Expected watches len is 0, but got: %d, %v", len(w.watches), w.watches)
- }
- if len(w.paths) != 0 {
- t.Fatalf("Expected paths len is 0, but got: %d, %v", len(w.paths), w.paths)
- }
+ func() {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ if len(w.watches) != 0 {
+ t.Fatalf("Expected watches len is 0, but got: %d, %v", len(w.watches), w.watches)
+ }
+ if len(w.paths) != 0 {
+ t.Fatalf("Expected paths len is 0, but got: %d, %v", len(w.paths), w.paths)
+ }
+ }()
w.Close()
wg.Wait()
}
}
+// Make sure Close() doesn't race; hard to write a good reproducible test for
+// this, but running it 150 times seems to reproduce it in ~75% of cases and
+// isn't too slow (~0.06s on my system).
+func TestCloseRace(t *testing.T) {
+ for i := 0; i < 150; i++ {
+ w, err := NewWatcher()
+ if err != nil {
+ t.Fatal(err)
+ }
+ go w.Close()
+ go w.Close()
+ go w.Close()
+ }
+}
+
func testRename(file1, file2 string) error {
switch runtime.GOOS {
case "windows", "plan9":