Skip to content

Race between Add() and WatchList() on Windows since v1.9.0 #709

@k3an3

Description

@k3an3

Describe the bug

The Go race detector flags on concurrent read/write across goroutines when one is calling .Add() while the other calls .WatchList(). The issue was not present on v1.7.0 using the same test, but was introduced with v1.9.0 and persists on the main branch at v1.9.1-0.20250811202840-88b7b41337a8. I was unable to test on v1.8.0 as the same test just hangs.

==================
WARNING: DATA RACE
Read at 0x00c0001a40c0 by goroutine 10:
  github.com/fsnotify/fsnotify.(*readDirChangesW).WatchList()
      C:/Users/user/go/pkg/mod/github.com/fsnotify/fsnotify@v1.9.0/backend_windows.go:178 +0x4ee
  github.com/fsnotify/fsnotify.(*Watcher).WatchList()
      C:/Users/user/go/pkg/mod/github.com/fsnotify/fsnotify@v1.9.0/fsnotify.go:343 +0x4a
  tst_test.TestWatchListRace.func1()
      C:/Users/user/dev/tst/race_test.go:36 +0x33

Previous write at 0x00c0001a40c0 by goroutine 9:
  github.com/fsnotify/fsnotify.(*readDirChangesW).addWatch()
      C:/Users/user/go/pkg/mod/github.com/fsnotify/fsnotify@v1.9.0/backend_windows.go:363 +0x814
  github.com/fsnotify/fsnotify.(*readDirChangesW).readEvents()
      C:/Users/user/go/pkg/mod/github.com/fsnotify/fsnotify@v1.9.0/backend_windows.go:521 +0x655
  github.com/fsnotify/fsnotify.newBackend.gowrap1()
      C:/Users/user/go/pkg/mod/github.com/fsnotify/fsnotify@v1.9.0/backend_windows.go:53 +0x33

Goroutine 10 (running) created at:
  tst_test.TestWatchListRace()
      C:/Users/user/dev/tst/race_test.go:30 +0x2f2
  testing.tRunner()
      C:/Program Files/Go/src/testing/testing.go:1934 +0x1c3
  testing.(*T).Run.gowrap1()
      C:/Program Files/Go/src/testing/testing.go:1997 +0x44

Goroutine 9 (running) created at:
  github.com/fsnotify/fsnotify.newBackend()
      C:/Users/user/go/pkg/mod/github.com/fsnotify/fsnotify@v1.9.0/backend_windows.go:53 +0x32d
  github.com/fsnotify/fsnotify.NewWatcher()
      C:/Users/user/go/pkg/mod/github.com/fsnotify/fsnotify@v1.9.0/fsnotify.go:254 +0x64
  tst_test.TestWatchListRace()
      C:/Users/user/dev/tst/race_test.go:18 +0x56
  testing.tRunner()
      C:/Program Files/Go/src/testing/testing.go:1934 +0x1c3
  testing.(*T).Run.gowrap1()
      C:/Program Files/Go/src/testing/testing.go:1997 +0x44
==================
--- FAIL: TestWatchListRace (0.00s)
    testing.go:1617: race detected during execution of test
FAIL
exit status 1
FAIL    tst     0.327s

Code to Reproduce

go get github.com/fsnotify/fsnotify@main
//go:build windows
// +build windows

package fsnotify_race_test

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/fsnotify/fsnotify"
)

// Reproduces: readDirChangesW.addWatch (write) vs WatchList (read).
func TestWatchListRace(t *testing.T) {
	root := t.TempDir()

	w, err := fsnotify.NewWatcher()
	if err != nil {
		t.Fatalf("NewWatcher: %v", err)
	}
	defer w.Close()

	if err := w.Add(root); err != nil {
		t.Fatalf("Add(root): %v", err)
	}

	// --- reader: continuously calls WatchList()
	done := make(chan struct{})
	go func() {
		for {
			select {
			case <-done:
				return
			default:
				_ = w.WatchList()
			}
		}
	}()

	// --- writer: Add() call => addWatch()
	dir := filepath.Join(root, "test")
	os.Mkdir(dir, 0o755)
	_ = w.Add(dir)

	close(done)
}

File operations to reproduce

N/A

Which operating system and version are you using?

OS Name: Microsoft Windows 11 Pro
OS Version: 10.0.26100 N/A Build 26100
OS Manufacturer: Microsoft Corporation
OS Configuration: Standalone Workstation
OS Build Type: Multiprocessor Free

Which fsnotify version are you using?

v1.9.0

Did you try the latest main branch?

Yes

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions