// [ErrEventOverflow] is used to indicate there are too many events:
//
// - inotify: there are too many queued events (fs.inotify.max_queued_events sysctl)
- // - windows: The buffer size is too small.
+ // - windows: The buffer size is too small; [WithBufferSize] can be used to increase it.
// - kqueue, fen: not used.
Errors chan error
//
// Returns [ErrClosed] if [Watcher.Close] was called.
//
+// See [AddWith] for a version that allows adding options.
+//
// # Watching directories
//
// All files in a directory are monitored, including new files that are created
//
// Instead, watch the parent directory and use Event.Name to filter out files
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
-func (w *Watcher) Add(name string) error {
+func (w *Watcher) Add(name string) error { return w.AddWith(name) }
+
+// AddWith is like [Add], but allows adding options. When using Add() the
+// defaults described below are used.
+//
+// Possible options are:
+//
+// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
+// other platforms. The default is 64K (65536 bytes).
+func (w *Watcher) AddWith(name string, opts ...addOpt) error {
if w.isClosed() {
return ErrClosed
}
return nil
}
+ _ = getOptions(opts...)
+
// Currently we resolve symlinks that were explicitly requested to be
// watched. Otherwise we would use LStat here.
stat, err := os.Stat(name)
// [ErrEventOverflow] is used to indicate there are too many events:
//
// - inotify: there are too many queued events (fs.inotify.max_queued_events sysctl)
- // - windows: The buffer size is too small.
+ // - windows: The buffer size is too small; [WithBufferSize] can be used to increase it.
// - kqueue, fen: not used.
Errors chan error
//
// Returns [ErrClosed] if [Watcher.Close] was called.
//
+// See [AddWith] for a version that allows adding options.
+//
// # Watching directories
//
// All files in a directory are monitored, including new files that are created
//
// Instead, watch the parent directory and use Event.Name to filter out files
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
-func (w *Watcher) Add(name string) error {
- name = filepath.Clean(name)
+func (w *Watcher) Add(name string) error { return w.AddWith(name) }
+
+// AddWith is like [Add], but allows adding options. When using Add() the
+// defaults described below are used.
+//
+// Possible options are:
+//
+// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
+// other platforms. The default is 64K (65536 bytes).
+func (w *Watcher) AddWith(name string, opts ...addOpt) error {
if w.isClosed() {
return ErrClosed
}
+ name = filepath.Clean(name)
+ _ = getOptions(opts...)
+
var flags uint32 = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
// [ErrEventOverflow] is used to indicate there are too many events:
//
// - inotify: there are too many queued events (fs.inotify.max_queued_events sysctl)
- // - windows: The buffer size is too small.
+ // - windows: The buffer size is too small; [WithBufferSize] can be used to increase it.
// - kqueue, fen: not used.
Errors chan error
//
// Returns [ErrClosed] if [Watcher.Close] was called.
//
+// See [AddWith] for a version that allows adding options.
+//
// # Watching directories
//
// All files in a directory are monitored, including new files that are created
//
// Instead, watch the parent directory and use Event.Name to filter out files
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
-func (w *Watcher) Add(name string) error {
+func (w *Watcher) Add(name string) error { return w.AddWith(name) }
+
+// AddWith is like [Add], but allows adding options. When using Add() the
+// defaults described below are used.
+//
+// Possible options are:
+//
+// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
+// other platforms. The default is 64K (65536 bytes).
+func (w *Watcher) AddWith(name string, opts ...addOpt) error {
+ _ = getOptions(opts...)
+
w.mu.Lock()
w.userWatches[name] = struct{}{}
w.mu.Unlock()
// [ErrEventOverflow] is used to indicate there are too many events:
//
// - inotify: there are too many queued events (fs.inotify.max_queued_events sysctl)
- // - windows: The buffer size is too small.
+ // - windows: The buffer size is too small; [WithBufferSize] can be used to increase it.
// - kqueue, fen: not used.
Errors chan error
}
//
// Returns [ErrClosed] if [Watcher.Close] was called.
//
+// See [AddWith] for a version that allows adding options.
+//
// # Watching directories
//
// All files in a directory are monitored, including new files that are created
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
func (w *Watcher) Add(name string) error { return nil }
+// AddWith is like [Add], but allows adding options. When using Add() the
+// defaults described below are used.
+//
+// Possible options are:
+//
+// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
+// other platforms. The default is 64K (65536 bytes).
+func (w *Watcher) AddWith(name string, opts ...addOpt) error {
+ return nil
+}
+
// Remove stops monitoring the path for changes.
//
// Directories are always removed non-recursively. For example, if you added
// [ErrEventOverflow] is used to indicate there are too many events:
//
// - inotify: there are too many queued events (fs.inotify.max_queued_events sysctl)
- // - windows: The buffer size is too small.
+ // - windows: The buffer size is too small; [WithBufferSize] can be used to increase it.
// - kqueue, fen: not used.
Errors chan error
//
// Returns [ErrClosed] if [Watcher.Close] was called.
//
+// See [AddWith] for a version that allows adding options.
+//
// # Watching directories
//
// All files in a directory are monitored, including new files that are created
//
// Instead, watch the parent directory and use Event.Name to filter out files
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
-func (w *Watcher) Add(name string) error {
+func (w *Watcher) Add(name string) error { return w.AddWith(name) }
+
+// AddWith is like [Add], but allows adding options. When using Add() the
+// defaults described below are used.
+//
+// Possible options are:
+//
+// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
+// other platforms. The default is 64K (65536 bytes).
+func (w *Watcher) AddWith(name string, opts ...addOpt) error {
if w.isClosed() {
return ErrClosed
}
+ with := getOptions(opts...)
+ if with.bufsize < 4096 {
+ return fmt.Errorf("fsnotify.WithBufferSize: buffer size cannot be smaller than 4096 bytes")
+ }
+
in := &input{
- op: opAddWatch,
- path: filepath.Clean(name),
- flags: sysFSALLEVENTS,
- reply: make(chan error),
+ op: opAddWatch,
+ path: filepath.Clean(name),
+ flags: sysFSALLEVENTS,
+ reply: make(chan error),
+ bufsize: with.bufsize,
}
w.input <- in
if err := w.wakeupReader(); err != nil {
)
type input struct {
- op int
- path string
- flags uint32
- reply chan error
+ op int
+ path string
+ flags uint32
+ bufsize int
+ reply chan error
}
type inode struct {
mask uint64 // Directory itself is being watched with these notify flags
names map[string]uint64 // Map of names being watched and their notify flags
rename string // Remembers the old name while renaming a file
- buf [65536]byte // 64K buffer
+ buf []byte // buffer, allocated later
}
type (
}
// Must run within the I/O thread.
-func (w *Watcher) addWatch(pathname string, flags uint64) error {
+func (w *Watcher) addWatch(pathname string, flags uint64, bufsize int) error {
dir, err := w.getDir(pathname)
if err != nil {
return err
ino: ino,
path: dir,
names: make(map[string]uint64),
+ buf: make([]byte, bufsize),
}
w.mu.Lock()
w.watches.set(ino, watchEntry)
return nil
}
- rdErr := windows.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
- uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
+ // We need to pass the array, rather than the slice.
+ hdr := (*reflect.SliceHeader)(unsafe.Pointer(&watch.buf))
+ rdErr := windows.ReadDirectoryChanges(watch.ino.handle,
+ (*byte)(unsafe.Pointer(hdr.Data)), uint32(hdr.Len),
+ false, mask, nil, &watch.ov, 0)
if rdErr != nil {
err := os.NewSyscallError("ReadDirectoryChanges", rdErr)
if rdErr == windows.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
case in := <-w.input:
switch in.op {
case opAddWatch:
- in.reply <- w.addWatch(in.path, uint64(in.flags))
+ in.reply <- w.addWatch(in.path, uint64(in.flags), in.bufsize)
case opRemoveWatch:
in.reply <- w.remWatch(in.path)
}
func (e Event) String() string {
return fmt.Sprintf("%-13s %q", e.Op.String(), e.Name)
}
+
+type (
+ addOpt func(opt *withOpts)
+ withOpts struct {
+ bufsize int
+ }
+)
+
+var defaultOpts = withOpts{
+ bufsize: 65536, // 64K
+}
+
+func getOptions(opts ...addOpt) withOpts {
+ with := defaultOpts
+ for _, o := range opts {
+ o(&with)
+ }
+ return with
+}
+
+// WithBufferSize sets the buffer size for the Windows backend. This is a no-op
+// for other backends.
+//
+// The default value is 64K (65536 bytes) which is the highest value that works
+// on all filesystems and should be enough for most applications, but if you
+// have a large burst of events it may not be enough. You can increase it if
+// you're hitting "queue or buffer overflow" errors ([ErrEventOverflow]).
+func WithBufferSize(bytes int) addOpt {
+ return func(opt *withOpts) { opt.bufsize = bytes }
+}
//
// Returns [ErrClosed] if [Watcher.Close] was called.
//
+// See [AddWith] for a version that allows adding options.
+//
// # Watching directories
//
// All files in a directory are monitored, including new files that are created
EOF
)
+addwith=$(<<EOF
+// AddWith is like [Add], but allows adding options. When using Add() the
+// defaults described below are used.
+//
+// Possible options are:
+//
+// - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
+// other platforms. The default is 64K (65536 bytes).
+EOF
+)
+
remove=$(<<EOF
// Remove stops monitoring the path for changes.
//
// [ErrEventOverflow] is used to indicate there are too many events:
//
// - inotify: there are too many queued events (fs.inotify.max_queued_events sysctl)
- // - windows: The buffer size is too small.
+ // - windows: The buffer size is too small; [WithBufferSize] can be used to increase it.
// - kqueue, fen: not used.
EOF
)
set-cmt '^type Watcher struct ' $watcher
set-cmt '^func NewWatcher(' $new
set-cmt '^func (w \*Watcher) Add(' $add
+set-cmt '^func (w \*Watcher) AddWith(' $addwith
set-cmt '^func (w \*Watcher) Remove(' $remove
set-cmt '^func (w \*Watcher) Close(' $close
set-cmt '^func (w \*Watcher) WatchList(' $watchlist