]> go.fuhry.dev Git - fsnotify.git/commitdiff
windows: protect access to isClosed with mutex (#454)
authorRobert Fratto <robertfratto@gmail.com>
Thu, 21 Jul 2022 03:06:40 +0000 (23:06 -0400)
committerGitHub <noreply@github.com>
Thu, 21 Jul 2022 03:06:40 +0000 (05:06 +0200)
isClosed may be read and modified concurrently if the Windows Watcher is
closed at the same time as a new entry being added. This change protects
isClosed to prevent race conditions.

The order of fields for the Windows Watcher have been rearranged to
group fields protected by the mutex together.

windows.go

index 02ce7deb0bbf0a4f1cdc98f0ed7f652f457d8c84..7ff70093a1fa744e8bc46639736e142e49ee3941 100644 (file)
@@ -21,14 +21,16 @@ import (
 
 // Watcher watches a set of files, delivering events to a channel.
 type Watcher struct {
-       Events   chan Event
-       Errors   chan error
-       isClosed bool           // Set to true when Close() is first called
-       mu       sync.Mutex     // Map access
-       port     syscall.Handle // Handle to completion port
-       watches  watchMap       // Map of watches (key: i-number)
-       input    chan *input    // Inputs to the reader are sent on this channel
-       quit     chan chan<- error
+       Events chan Event
+       Errors chan error
+
+       port  syscall.Handle // Handle to completion port
+       input chan *input    // Inputs to the reader are sent on this channel
+       quit  chan chan<- error
+
+       mu       sync.Mutex // Protects access to watches, isClosed
+       watches  watchMap   // Map of watches (key: i-number)
+       isClosed bool       // Set to true when Close() is first called
 }
 
 // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
@@ -51,6 +53,9 @@ func NewWatcher() (*Watcher, error) {
 
 // Close removes all watches and closes the events channel.
 func (w *Watcher) Close() error {
+       w.mu.Lock()
+       defer w.mu.Unlock()
+
        if w.isClosed {
                return nil
        }
@@ -67,9 +72,12 @@ func (w *Watcher) Close() error {
 
 // Add starts watching the named file or directory (non-recursively).
 func (w *Watcher) Add(name string) error {
+       w.mu.Lock()
        if w.isClosed {
                return errors.New("watcher already closed")
        }
+       w.mu.Unlock()
+
        in := &input{
                op:    opAddWatch,
                path:  filepath.Clean(name),