Skip to content

Race when calling Add from a goroutine #672

@stevenctl

Description

@stevenctl

Describe the bug

There is a race when calling Add on a watcher within a goroutine

Running go test -race ./projects/sds/pkg/run -count 1 -v on https://github.com/solo-io/gloo/ when attempting to bump to 1.8.0 (occasionally) causes this, but on 1.7.0 I haven't been able to reproduce this.

https://github.com/solo-io/gloo/blob/main/projects/sds/pkg/run/run.go#L56

==================
WARNING: DATA RACE
Write at 0x00c0006bc870 by goroutine 89:
  runtime.mapassign_faststr()
      /home/landow/go/pkg/mod/golang.org/[email protected]/src/runtime/map_faststr.go:223 +0x0
  github.com/fsnotify/fsnotify.(*watches).updatePath()
      /home/landow/go/pkg/mod/github.com/fsnotify/[email protected]/backend_inotify.go:163 +0x2b8
  github.com/fsnotify/fsnotify.(*inotify).register()
      /home/landow/go/pkg/mod/github.com/fsnotify/[email protected]/backend_inotify.go:335 +0x1bd
  github.com/fsnotify/fsnotify.(*inotify).add()
      /home/landow/go/pkg/mod/github.com/fsnotify/[email protected]/backend_inotify.go:331 +0xee
  github.com/fsnotify/fsnotify.(*inotify).AddWith()
      /home/landow/go/pkg/mod/github.com/fsnotify/[email protected]/backend_inotify.go:296 +0x405
  github.com/fsnotify/fsnotify.(*inotify).Add()
      /home/landow/go/pkg/mod/github.com/fsnotify/[email protected]/backend_inotify.go:253 +0x44
  github.com/fsnotify/fsnotify.(*Watcher).Add()
      /home/landow/go/pkg/mod/github.com/fsnotify/[email protected]/fsnotify.go:313 +0x3b8
  github.com/solo-io/gloo/projects/sds/pkg/run.watchFiles()
      /home/landow/solo/gloo/main/projects/sds/pkg/run/run.go:82 +0x375
  github.com/solo-io/gloo/projects/sds/pkg/run.Run.func1()
      /home/landow/solo/gloo/main/projects/sds/pkg/run/run.go:59 +0x584

Previous read at 0x00c0006bc870 by goroutine 88:
  runtime.mapaccess2_faststr()
      /home/landow/go/pkg/mod/golang.org/[email protected]/src/runtime/map_faststr.go:117 +0x0
  github.com/fsnotify/fsnotify.(*inotify).readEvents()
      /home/landow/go/pkg/mod/github.com/fsnotify/[email protected]/backend_inotify.go:534 +0xd04
  github.com/fsnotify/fsnotify.newBufferedBackend.gowrap1()
      /home/landow/go/pkg/mod/github.com/fsnotify/[email protected]/backend_inotify.go:195 +0x33

Goroutine 89 (running) created at:
  github.com/solo-io/gloo/projects/sds/pkg/run.Run()
      /home/landow/solo/gloo/main/projects/sds/pkg/run/run.go:52 +0x3f2
  github.com/solo-io/gloo/projects/sds/pkg/run_test.init.func1.4.1()
      /home/landow/solo/gloo/main/projects/sds/pkg/run/run_e2e_test.go:134 +0x204

Goroutine 88 (running) created at:
  github.com/fsnotify/fsnotify.newBufferedBackend()
      /home/landow/go/pkg/mod/github.com/fsnotify/[email protected]/backend_inotify.go:195 +0x3c6
  github.com/fsnotify/fsnotify.newBackend()
      /home/landow/go/pkg/mod/github.com/fsnotify/[email protected]/backend_inotify.go:174 +0x51
  github.com/fsnotify/fsnotify.NewWatcher()
      /home/landow/go/pkg/mod/github.com/fsnotify/[email protected]/fsnotify.go:253 +0x42
  github.com/solo-io/gloo/projects/sds/pkg/run.Run()
      /home/landow/solo/gloo/main/projects/sds/pkg/run/run.go:37 +0x138
  github.com/solo-io/gloo/projects/sds/pkg/run_test.init.func1.4.1()
      /home/landow/solo/gloo/main/projects/sds/pkg/run/run_e2e_test.go:134 +0x204
==================

Code to Reproduce

go test -race -v ./race_test.go

package main

import (
	"os"
	"testing"
	"time"

	"github.com/fsnotify/fsnotify"
)

func TestRaces(t *testing.T) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		panic(err)
	}
	defer watcher.Close()

	touch()
	err = watcher.Add("./foo.txt")
	if err != nil {
		panic(err)
	}

	go func() {
		for {
			select {
			case event := <-watcher.Events:
				println("event:", event.String())
				err := watcher.Add("./foo.txt")
				if err != nil {
					println("readd err:", err.Error())
				}
			case err := <-watcher.Errors:
				println("watch err:", err)
			}
		}
	}()

	for {
		remove()
		touch()
		time.Sleep(1 * time.Second)
	}
}

func touch() {
	// Touch the file to ensure it exists
	file, err := os.Create("./foo.txt")
	if err != nil {
		println("touch err:", err)
		return
	}
	file.Close()
	println("did touch")
}

func remove() {
	if _, err := os.Stat("./foo.txt"); os.IsNotExist(err) {
		return
	}
	err := os.Remove("./foo.txt")
	if err != nil {
		println("rm err:", err.Error())
		return
	}
	println("did rm")
}

File operations to reproduce

Adding and removing a file.

Which operating system and version are you using?

Linux fractal 6.1.90-1-MANJARO #1 SMP PREEMPT_DYNAMIC Fri May  3 05:13:01 UT
C 2024 x86_64 GNU/Linux

Which fsnotify version are you using?

1.8.0

Did you try the latest main branch?

Yes

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions