From 887858705e35407d7482aec89ebbbec5fc884d07 Mon Sep 17 00:00:00 2001 From: Martin Tournoij Date: Thu, 13 Oct 2022 03:10:35 +0200 Subject: [PATCH] Tweak the docs a bit --- README.md | 31 ++++++++++++++----------------- backend_fen.go | 28 ++++++++++++++++------------ backend_inotify.go | 30 +++++++++++++++++------------- backend_kqueue.go | 30 +++++++++++++++++------------- backend_other.go | 8 ++++---- backend_windows.go | 30 +++++++++++++++++------------- cmd/fsnotify/dedup.go | 5 +++-- cmd/fsnotify/file.go | 5 ++++- cmd/fsnotify/main.go | 5 +++-- cmd/fsnotify/watch.go | 2 +- fsnotify.go | 2 +- mkdoc.zsh | 30 +++++++++++++++++------------- 12 files changed, 114 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index e244758..d4e6080 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,12 @@ fsnotify is a Go library to provide cross-platform filesystem notifications on Windows, Linux, macOS, and BSD systems. -fsnotify requires Go 1.16 or newer. +Go 1.16 or newer is required; the full documentation is at +https://pkg.go.dev/github.com/fsnotify/fsnotify -API docs: https://pkg.go.dev/github.com/fsnotify/fsnotify - -It's best to read the documentation at pkg.go.dev, as it's pinned to the last +**It's best to read the documentation at pkg.go.dev, as it's pinned to the last released version, whereas this README is for the last development version which -may include additions/changes. +may include additions/changes.** --- @@ -89,15 +88,15 @@ FAQ ### Will a file still be watched when it's moved to another directory? No, not unless you are watching the location it was moved to. -### Are all subdirectories watched too? +### Are subdirectories watched too? No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap: [#18]). [#18]: https://github.com/fsnotify/fsnotify/issues/18 -### Do I have to watch the Error and Event channels in a separate goroutine? -As of now, yes (you can read both channels in the same goroutine, you don't need -a separate goroutine for both channels; see the example). +### Do I have to watch the Error and Event channels in a goroutine? +As of now, yes (you can read both channels in the same goroutine using `select`, +you don't need a separate goroutine for both channels; see the example). ### Why don't notifications work with NFS, SMB, FUSE, /proc, or /sys? fsnotify requires support from underlying OS to work. The current NFS and SMB @@ -112,18 +111,20 @@ Platform-specific notes ----------------------- ### Linux When a file is removed a REMOVE event won't be emitted until all file -descriptors are closed. It will emit a CHMOD though: +descriptors are closed; it will emit a CHMOD instead: fp := os.Open("file") os.Remove("file") // CHMOD fp.Close() // REMOVE +This is the event that inotify sends, so not much can be changed about this. + The `fs.inotify.max_user_watches` sysctl variable specifies the upper limit for the number of watches per user, and `fs.inotify.max_user_instances` specifies the maximum number of inotify instances per user. Every Watcher you create is an "instance", and every path you add is a "watch". -These are also exposed in /proc as `/proc/sys/fs/inotify/max_user_watches` and +These are also exposed in `/proc` as `/proc/sys/fs/inotify/max_user_watches` and `/proc/sys/fs/inotify/max_user_instances` To increase them you can use `sysctl` or write the value to proc file: @@ -133,7 +134,8 @@ To increase them you can use `sysctl` or write the value to proc file: sysctl fs.inotify.max_user_instances=128 To make the changes persist on reboot edit `/etc/sysctl.conf` or -`/usr/lib/sysctl.d/50-default.conf` (some systemd systems): +`/usr/lib/sysctl.d/50-default.conf` (details differ per Linux distro; check your +distro's documentation): fs.inotify.max_user_watches=124983 fs.inotify.max_user_instances=128 @@ -157,8 +159,3 @@ have a native FSEvents implementation (see [#11]). [#11]: https://github.com/fsnotify/fsnotify/issues/11 [#15]: https://github.com/fsnotify/fsnotify/issues/15 - -Related Projects ----------------- -- [notify](https://github.com/rjeczalik/notify) -- [fsevents](https://github.com/fsnotify/fsevents) diff --git a/backend_fen.go b/backend_fen.go index af2d092..1a95ad8 100644 --- a/backend_fen.go +++ b/backend_fen.go @@ -7,7 +7,7 @@ import ( "errors" ) -// Watcher watches a set of files, delivering events to a channel. +// Watcher watches a set of paths, delivering events on a channel. // // A watcher should not be copied (e.g. pass it by pointer, rather than by // value). @@ -21,6 +21,8 @@ import ( // os.Remove("file") // Triggers Chmod // fp.Close() // Triggers Remove // +// This is the event that inotify sends, so not much can be changed about this. +// // The fs.inotify.max_user_watches sysctl variable specifies the upper limit // for the number of watches per user, and fs.inotify.max_user_instances // specifies the maximum number of inotify instances per user. Every Watcher you @@ -36,7 +38,8 @@ import ( // sysctl fs.inotify.max_user_instances=128 // // To make the changes persist on reboot edit /etc/sysctl.conf or -// /usr/lib/sysctl.d/50-default.conf (on some systemd systems): +// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check +// your distro's documentation): // // fs.inotify.max_user_watches=124983 // fs.inotify.max_user_instances=128 @@ -67,7 +70,7 @@ type Watcher struct { // Events sends the filesystem change events. // // fsnotify can send the following events; a "path" here can refer to a - // file, directory, symbolic link, or special files like a FIFO. + // file, directory, symbolic link, or special file like a FIFO. // // fsnotify.Create A new path was created; this may be followed by one // or more Write events if data also gets written to a @@ -76,7 +79,7 @@ type Watcher struct { // fsnotify.Remove A path was removed. // // fsnotify.Rename A path was renamed. A rename is always sent with the - // old path as [Event.Name], and a Create event will be + // old path as Event.Name, and a Create event will be // sent with the new name. Renames are only sent for // paths that are currently watched; e.g. moving an // unmonitored file into a monitored directory will @@ -93,10 +96,11 @@ type Watcher struct { // probably want to wait until you've stopped receiving // them (see the dedup example in cmd/fsnotify). // - // fsnotify.Chmod Attributes were changes (never sent on Windows). On - // Linux this is also sent when a file is removed (or - // more accurately, when a link to an inode is - // removed), and on kqueue when a file is truncated. + // fsnotify.Chmod Attributes were changed. On Linux this is also sent + // when a file is removed (or more accurately, when a + // link to an inode is removed). On kqueue it's sent + // and on kqueue when a file is truncated. On Windows + // it's never sent. Events chan Event // Errors sends any errors. @@ -121,7 +125,7 @@ func (w *Watcher) Close() error { // // A path will remain watched if it gets renamed to somewhere else on the same // filesystem, but the monitor will get removed if the path gets deleted and -// re-created. +// re-created, or if it's moved to a different filesystem. // // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special // filesystems (/proc, /sys, etc.) generally don't work. @@ -137,12 +141,12 @@ func (w *Watcher) Close() error { // Watching individual files (rather than directories) is generally not // recommended as many tools update files atomically. Instead of "just" writing // to the file a temporary file will be written to first, and if successful the -// temporary file is moved to to destination, removing the original, or some +// temporary file is moved to to destination removing the original, or some // variant thereof. The watcher on the original file is now lost, as it no // longer exists. // -// 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 +// 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 { return nil } diff --git a/backend_inotify.go b/backend_inotify.go index 855e654..54c77fb 100644 --- a/backend_inotify.go +++ b/backend_inotify.go @@ -16,7 +16,7 @@ import ( "golang.org/x/sys/unix" ) -// Watcher watches a set of files, delivering events to a channel. +// Watcher watches a set of paths, delivering events on a channel. // // A watcher should not be copied (e.g. pass it by pointer, rather than by // value). @@ -30,6 +30,8 @@ import ( // os.Remove("file") // Triggers Chmod // fp.Close() // Triggers Remove // +// This is the event that inotify sends, so not much can be changed about this. +// // The fs.inotify.max_user_watches sysctl variable specifies the upper limit // for the number of watches per user, and fs.inotify.max_user_instances // specifies the maximum number of inotify instances per user. Every Watcher you @@ -45,7 +47,8 @@ import ( // sysctl fs.inotify.max_user_instances=128 // // To make the changes persist on reboot edit /etc/sysctl.conf or -// /usr/lib/sysctl.d/50-default.conf (on some systemd systems): +// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check +// your distro's documentation): // // fs.inotify.max_user_watches=124983 // fs.inotify.max_user_instances=128 @@ -76,7 +79,7 @@ type Watcher struct { // Events sends the filesystem change events. // // fsnotify can send the following events; a "path" here can refer to a - // file, directory, symbolic link, or special files like a FIFO. + // file, directory, symbolic link, or special file like a FIFO. // // fsnotify.Create A new path was created; this may be followed by one // or more Write events if data also gets written to a @@ -85,7 +88,7 @@ type Watcher struct { // fsnotify.Remove A path was removed. // // fsnotify.Rename A path was renamed. A rename is always sent with the - // old path as [Event.Name], and a Create event will be + // old path as Event.Name, and a Create event will be // sent with the new name. Renames are only sent for // paths that are currently watched; e.g. moving an // unmonitored file into a monitored directory will @@ -102,10 +105,11 @@ type Watcher struct { // probably want to wait until you've stopped receiving // them (see the dedup example in cmd/fsnotify). // - // fsnotify.Chmod Attributes were changes (never sent on Windows). On - // Linux this is also sent when a file is removed (or - // more accurately, when a link to an inode is - // removed), and on kqueue when a file is truncated. + // fsnotify.Chmod Attributes were changed. On Linux this is also sent + // when a file is removed (or more accurately, when a + // link to an inode is removed). On kqueue it's sent + // and on kqueue when a file is truncated. On Windows + // it's never sent. Events chan Event // Errors sends any errors. @@ -209,7 +213,7 @@ func (w *Watcher) Close() error { // // A path will remain watched if it gets renamed to somewhere else on the same // filesystem, but the monitor will get removed if the path gets deleted and -// re-created. +// re-created, or if it's moved to a different filesystem. // // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special // filesystems (/proc, /sys, etc.) generally don't work. @@ -225,12 +229,12 @@ func (w *Watcher) Close() error { // Watching individual files (rather than directories) is generally not // recommended as many tools update files atomically. Instead of "just" writing // to the file a temporary file will be written to first, and if successful the -// temporary file is moved to to destination, removing the original, or some +// temporary file is moved to to destination removing the original, or some // variant thereof. The watcher on the original file is now lost, as it no // longer exists. // -// 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 +// 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) if w.isClosed() { @@ -312,7 +316,7 @@ func (w *Watcher) Remove(name string) error { return nil } -// WatchList returns all paths added with Add() (and are not yet removed). +// WatchList returns all paths added with [Add] (and are not yet removed). func (w *Watcher) WatchList() []string { w.mu.Lock() defer w.mu.Unlock() diff --git a/backend_kqueue.go b/backend_kqueue.go index 409c645..2908746 100644 --- a/backend_kqueue.go +++ b/backend_kqueue.go @@ -14,7 +14,7 @@ import ( "golang.org/x/sys/unix" ) -// Watcher watches a set of files, delivering events to a channel. +// Watcher watches a set of paths, delivering events on a channel. // // A watcher should not be copied (e.g. pass it by pointer, rather than by // value). @@ -28,6 +28,8 @@ import ( // os.Remove("file") // Triggers Chmod // fp.Close() // Triggers Remove // +// This is the event that inotify sends, so not much can be changed about this. +// // The fs.inotify.max_user_watches sysctl variable specifies the upper limit // for the number of watches per user, and fs.inotify.max_user_instances // specifies the maximum number of inotify instances per user. Every Watcher you @@ -43,7 +45,8 @@ import ( // sysctl fs.inotify.max_user_instances=128 // // To make the changes persist on reboot edit /etc/sysctl.conf or -// /usr/lib/sysctl.d/50-default.conf (on some systemd systems): +// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check +// your distro's documentation): // // fs.inotify.max_user_watches=124983 // fs.inotify.max_user_instances=128 @@ -74,7 +77,7 @@ type Watcher struct { // Events sends the filesystem change events. // // fsnotify can send the following events; a "path" here can refer to a - // file, directory, symbolic link, or special files like a FIFO. + // file, directory, symbolic link, or special file like a FIFO. // // fsnotify.Create A new path was created; this may be followed by one // or more Write events if data also gets written to a @@ -83,7 +86,7 @@ type Watcher struct { // fsnotify.Remove A path was removed. // // fsnotify.Rename A path was renamed. A rename is always sent with the - // old path as [Event.Name], and a Create event will be + // old path as Event.Name, and a Create event will be // sent with the new name. Renames are only sent for // paths that are currently watched; e.g. moving an // unmonitored file into a monitored directory will @@ -100,10 +103,11 @@ type Watcher struct { // probably want to wait until you've stopped receiving // them (see the dedup example in cmd/fsnotify). // - // fsnotify.Chmod Attributes were changes (never sent on Windows). On - // Linux this is also sent when a file is removed (or - // more accurately, when a link to an inode is - // removed), and on kqueue when a file is truncated. + // fsnotify.Chmod Attributes were changed. On Linux this is also sent + // when a file is removed (or more accurately, when a + // link to an inode is removed). On kqueue it's sent + // and on kqueue when a file is truncated. On Windows + // it's never sent. Events chan Event // Errors sends any errors. @@ -241,7 +245,7 @@ func (w *Watcher) Close() error { // // A path will remain watched if it gets renamed to somewhere else on the same // filesystem, but the monitor will get removed if the path gets deleted and -// re-created. +// re-created, or if it's moved to a different filesystem. // // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special // filesystems (/proc, /sys, etc.) generally don't work. @@ -257,12 +261,12 @@ func (w *Watcher) Close() error { // Watching individual files (rather than directories) is generally not // recommended as many tools update files atomically. Instead of "just" writing // to the file a temporary file will be written to first, and if successful the -// temporary file is moved to to destination, removing the original, or some +// temporary file is moved to to destination removing the original, or some // variant thereof. The watcher on the original file is now lost, as it no // longer exists. // -// 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 +// 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 { w.mu.Lock() w.userWatches[name] = struct{}{} @@ -332,7 +336,7 @@ func (w *Watcher) Remove(name string) error { return nil } -// WatchList returns all paths added with Add() (and are not yet removed). +// WatchList returns all paths added with [Add] (and are not yet removed). func (w *Watcher) WatchList() []string { w.mu.Lock() defer w.mu.Unlock() diff --git a/backend_other.go b/backend_other.go index d27f280..a9bb1c3 100644 --- a/backend_other.go +++ b/backend_other.go @@ -29,7 +29,7 @@ func (w *Watcher) Close() error { // // A path will remain watched if it gets renamed to somewhere else on the same // filesystem, but the monitor will get removed if the path gets deleted and -// re-created. +// re-created, or if it's moved to a different filesystem. // // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special // filesystems (/proc, /sys, etc.) generally don't work. @@ -45,12 +45,12 @@ func (w *Watcher) Close() error { // Watching individual files (rather than directories) is generally not // recommended as many tools update files atomically. Instead of "just" writing // to the file a temporary file will be written to first, and if successful the -// temporary file is moved to to destination, removing the original, or some +// temporary file is moved to to destination removing the original, or some // variant thereof. The watcher on the original file is now lost, as it no // longer exists. // -// 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 +// 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 { return nil } diff --git a/backend_windows.go b/backend_windows.go index a254053..ae39286 100644 --- a/backend_windows.go +++ b/backend_windows.go @@ -17,7 +17,7 @@ import ( "golang.org/x/sys/windows" ) -// Watcher watches a set of files, delivering events to a channel. +// Watcher watches a set of paths, delivering events on a channel. // // A watcher should not be copied (e.g. pass it by pointer, rather than by // value). @@ -31,6 +31,8 @@ import ( // os.Remove("file") // Triggers Chmod // fp.Close() // Triggers Remove // +// This is the event that inotify sends, so not much can be changed about this. +// // The fs.inotify.max_user_watches sysctl variable specifies the upper limit // for the number of watches per user, and fs.inotify.max_user_instances // specifies the maximum number of inotify instances per user. Every Watcher you @@ -46,7 +48,8 @@ import ( // sysctl fs.inotify.max_user_instances=128 // // To make the changes persist on reboot edit /etc/sysctl.conf or -// /usr/lib/sysctl.d/50-default.conf (on some systemd systems): +// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check +// your distro's documentation): // // fs.inotify.max_user_watches=124983 // fs.inotify.max_user_instances=128 @@ -77,7 +80,7 @@ type Watcher struct { // Events sends the filesystem change events. // // fsnotify can send the following events; a "path" here can refer to a - // file, directory, symbolic link, or special files like a FIFO. + // file, directory, symbolic link, or special file like a FIFO. // // fsnotify.Create A new path was created; this may be followed by one // or more Write events if data also gets written to a @@ -86,7 +89,7 @@ type Watcher struct { // fsnotify.Remove A path was removed. // // fsnotify.Rename A path was renamed. A rename is always sent with the - // old path as [Event.Name], and a Create event will be + // old path as Event.Name, and a Create event will be // sent with the new name. Renames are only sent for // paths that are currently watched; e.g. moving an // unmonitored file into a monitored directory will @@ -103,10 +106,11 @@ type Watcher struct { // probably want to wait until you've stopped receiving // them (see the dedup example in cmd/fsnotify). // - // fsnotify.Chmod Attributes were changes (never sent on Windows). On - // Linux this is also sent when a file is removed (or - // more accurately, when a link to an inode is - // removed), and on kqueue when a file is truncated. + // fsnotify.Chmod Attributes were changed. On Linux this is also sent + // when a file is removed (or more accurately, when a + // link to an inode is removed). On kqueue it's sent + // and on kqueue when a file is truncated. On Windows + // it's never sent. Events chan Event // Errors sends any errors. @@ -190,7 +194,7 @@ func (w *Watcher) Close() error { // // A path will remain watched if it gets renamed to somewhere else on the same // filesystem, but the monitor will get removed if the path gets deleted and -// re-created. +// re-created, or if it's moved to a different filesystem. // // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special // filesystems (/proc, /sys, etc.) generally don't work. @@ -206,12 +210,12 @@ func (w *Watcher) Close() error { // Watching individual files (rather than directories) is generally not // recommended as many tools update files atomically. Instead of "just" writing // to the file a temporary file will be written to first, and if successful the -// temporary file is moved to to destination, removing the original, or some +// temporary file is moved to to destination removing the original, or some // variant thereof. The watcher on the original file is now lost, as it no // longer exists. // -// 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 +// 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 { w.mu.Lock() if w.isClosed { @@ -252,7 +256,7 @@ func (w *Watcher) Remove(name string) error { return <-in.reply } -// WatchList returns all paths added with Add() (and are not yet removed). +// WatchList returns all paths added with [Add] (and are not yet removed). func (w *Watcher) WatchList() []string { w.mu.Lock() defer w.mu.Unlock() diff --git a/cmd/fsnotify/dedup.go b/cmd/fsnotify/dedup.go index 2d790a9..67269d4 100644 --- a/cmd/fsnotify/dedup.go +++ b/cmd/fsnotify/dedup.go @@ -9,7 +9,8 @@ import ( ) // Depending on the system, a single "write" can generate many Write events; for -// example compiling a large Go program can generate hundreds of Write events. +// example compiling a large Go program can generate hundreds of Write events on +// the binary. // // The general strategy to deal with this is to wait a short time for more write // events, resetting the wait period for every new event. @@ -28,7 +29,7 @@ func dedup(paths ...string) { // Start listening for events. go dedupLoop(w) - // Add all paths. + // Add all paths from the commandline. for _, p := range paths { err = w.Add(p) if err != nil { diff --git a/cmd/fsnotify/file.go b/cmd/fsnotify/file.go index 0f0b448..357e693 100644 --- a/cmd/fsnotify/file.go +++ b/cmd/fsnotify/file.go @@ -7,6 +7,9 @@ import ( "github.com/fsnotify/fsnotify" ) +// Watch one or more files, but instead of watching the file directly it watches +// the parent directory. This solves various issues where files are frequently +// renamed, such as editors saving them. func file(files ...string) { if len(files) < 1 { exit("must specify at least one file to watch") @@ -22,7 +25,7 @@ func file(files ...string) { // Start listening for events. go fileLoop(w, files) - // Add all files. + // Add all files from the commandline. for _, p := range files { st, err := os.Lstat(p) if err != nil { diff --git a/cmd/fsnotify/main.go b/cmd/fsnotify/main.go index 5c99ced..cdd9de7 100644 --- a/cmd/fsnotify/main.go +++ b/cmd/fsnotify/main.go @@ -1,3 +1,4 @@ +// Command fsnotify provides example usage of the fsnotify library. package main import ( @@ -8,8 +9,8 @@ import ( ) var usage = ` -fsnotify is a library to provide cross-platform file system notifications for -Go. This utility serves as an example and debugging tool. +fsnotify is a Go library to provide cross-platform file system notifications. +This command serves as an example and debugging tool. https://github.com/fsnotify/fsnotify diff --git a/cmd/fsnotify/watch.go b/cmd/fsnotify/watch.go index 3fe50e1..046a133 100644 --- a/cmd/fsnotify/watch.go +++ b/cmd/fsnotify/watch.go @@ -19,7 +19,7 @@ func watch(paths ...string) { // Start listening for events. go watchLoop(w) - // Add all paths. + // Add all paths from the commandline. for _, p := range paths { err = w.Add(p) if err != nil { diff --git a/fsnotify.go b/fsnotify.go index f776471..30a5bf0 100644 --- a/fsnotify.go +++ b/fsnotify.go @@ -22,7 +22,7 @@ type Event struct { // File operation that triggered the event. // - // This is a bitmask as some systems may send multiple operations at once. + // This is a bitmask and some systems may send multiple operations at once. // Use the Event.Has() method instead of comparing with ==. Op Op } diff --git a/mkdoc.zsh b/mkdoc.zsh index b6dff6e..b09ef76 100755 --- a/mkdoc.zsh +++ b/mkdoc.zsh @@ -6,7 +6,7 @@ setopt err_exit no_unset pipefail extended_glob # more time to write this than doing it manually, but ah well 🙃 watcher=$(<