From: Robert Fratto Date: Thu, 21 Jul 2022 03:06:40 +0000 (-0400) Subject: windows: protect access to isClosed with mutex (#454) X-Git-Tag: v1.7.2~114 X-Git-Url: https://go.fuhry.dev/?a=commitdiff_plain;h=5acfdc196a28e905f69348ba0b5c50714492ba26;p=fsnotify.git windows: protect access to isClosed with mutex (#454) 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. --- diff --git a/windows.go b/windows.go index 02ce7de..7ff7009 100644 --- a/windows.go +++ b/windows.go @@ -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),