## master
+* Rename source code files, rearrange code so exported APIs are at the top.
* Add done channel to example code. [#37](https://github.com/go-fsnotify/fsnotify/pull/37) (thanks @chenyukang)
## v1.0.3 / 2014-08-19
// Op describes a set of file operations.
type Op uint32
-// These are the file operations that can trigger a notification.
+// These are the generalized file operations that can trigger a notification.
const (
Create Op = 1 << iota
Write
"unsafe"
)
-const (
- agnosticEvents = syscall.IN_MOVED_TO | syscall.IN_MOVED_FROM |
- syscall.IN_CREATE | syscall.IN_ATTRIB | syscall.IN_MODIFY |
- syscall.IN_MOVE_SELF | syscall.IN_DELETE | syscall.IN_DELETE_SELF
-)
-
-// newEvent returns an platform-independent Event based on an inotify mask.
-func newEvent(name string, mask uint32) Event {
- e := Event{Name: name}
- if mask&syscall.IN_CREATE == syscall.IN_CREATE || mask&syscall.IN_MOVED_TO == syscall.IN_MOVED_TO {
- e.Op |= Create
- }
- if mask&syscall.IN_DELETE_SELF == syscall.IN_DELETE_SELF || mask&syscall.IN_DELETE == syscall.IN_DELETE {
- e.Op |= Remove
- }
- if mask&syscall.IN_MODIFY == syscall.IN_MODIFY {
- e.Op |= Write
- }
- if mask&syscall.IN_MOVE_SELF == syscall.IN_MOVE_SELF || mask&syscall.IN_MOVED_FROM == syscall.IN_MOVED_FROM {
- e.Op |= Rename
- }
- if mask&syscall.IN_ATTRIB == syscall.IN_ATTRIB {
- e.Op |= Chmod
- }
- return e
-}
-
-type watch struct {
- wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
- flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
-}
-
// Watcher watches a set of files, delivering events to a channel.
type Watcher struct {
Events chan Event
return errors.New("inotify instance already closed")
}
+ const agnosticEvents = syscall.IN_MOVED_TO | syscall.IN_MOVED_FROM |
+ syscall.IN_CREATE | syscall.IN_ATTRIB | syscall.IN_MODIFY |
+ syscall.IN_MOVE_SELF | syscall.IN_DELETE | syscall.IN_DELETE_SELF
+
var flags uint32 = agnosticEvents
w.mu.Lock()
return nil
}
+type watch struct {
+ wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
+ flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
+}
+
// readEvents reads from the inotify file descriptor, converts the
// received events into Event objects and sends them via the Events channel
func (w *Watcher) readEvents() {
}
return false
}
+
+// newEvent returns an platform-independent Event based on an inotify mask.
+func newEvent(name string, mask uint32) Event {
+ e := Event{Name: name}
+ if mask&syscall.IN_CREATE == syscall.IN_CREATE || mask&syscall.IN_MOVED_TO == syscall.IN_MOVED_TO {
+ e.Op |= Create
+ }
+ if mask&syscall.IN_DELETE_SELF == syscall.IN_DELETE_SELF || mask&syscall.IN_DELETE == syscall.IN_DELETE {
+ e.Op |= Remove
+ }
+ if mask&syscall.IN_MODIFY == syscall.IN_MODIFY {
+ e.Op |= Write
+ }
+ if mask&syscall.IN_MOVE_SELF == syscall.IN_MOVE_SELF || mask&syscall.IN_MOVED_FROM == syscall.IN_MOVED_FROM {
+ e.Op |= Rename
+ }
+ if mask&syscall.IN_ATTRIB == syscall.IN_ATTRIB {
+ e.Op |= Chmod
+ }
+ return e
+}
"syscall"
)
-const (
- // Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
- noteAllEvents = syscall.NOTE_DELETE | syscall.NOTE_WRITE | syscall.NOTE_ATTRIB | syscall.NOTE_RENAME
-
- // Block for 100 ms on each call to kevent
- keventWaitTime = 100e6
-)
-
-// newEvent returns an platform-independent Event based on kqueue Fflags.
-func newEvent(name string, mask uint32, create bool) Event {
- e := Event{Name: name}
- if create {
- e.Op |= Create
- }
- if mask&syscall.NOTE_DELETE == syscall.NOTE_DELETE {
- e.Op |= Remove
- }
- if mask&syscall.NOTE_WRITE == syscall.NOTE_WRITE {
- e.Op |= Write
- }
- if mask&syscall.NOTE_RENAME == syscall.NOTE_RENAME {
- e.Op |= Rename
- }
- if mask&syscall.NOTE_ATTRIB == syscall.NOTE_ATTRIB {
- e.Op |= Chmod
- }
- return e
-}
-
// Watcher watches a set of files, delivering events to a channel.
type Watcher struct {
Events chan Event
return nil
}
+// Add starts watching the named file or directory (non-recursively).
+func (w *Watcher) Add(name string) error {
+ w.ewmut.Lock()
+ w.externalWatches[name] = true
+ w.ewmut.Unlock()
+ return w.addWatch(name, noteAllEvents)
+}
+
+// Remove stops watching the the named file or directory (non-recursively).
+func (w *Watcher) Remove(name string) error {
+ name = filepath.Clean(name)
+ w.wmut.Lock()
+ watchfd, ok := w.watches[name]
+ w.wmut.Unlock()
+ if !ok {
+ return fmt.Errorf("can't remove non-existent kevent watch for: %s", name)
+ }
+ var kbuf [1]syscall.Kevent_t
+ watchEntry := &kbuf[0]
+ syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_DELETE)
+ entryFlags := watchEntry.Flags
+ success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil)
+ if success == -1 {
+ return os.NewSyscallError("kevent_rm_watch", errno)
+ } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR {
+ return errors.New("kevent rm error")
+ }
+ syscall.Close(watchfd)
+ w.wmut.Lock()
+ delete(w.watches, name)
+ w.wmut.Unlock()
+ w.enmut.Lock()
+ delete(w.enFlags, name)
+ w.enmut.Unlock()
+ w.pmut.Lock()
+ delete(w.paths, watchfd)
+ fInfo := w.finfo[watchfd]
+ delete(w.finfo, watchfd)
+ w.pmut.Unlock()
+
+ // Find all watched paths that are in this directory that are not external.
+ if fInfo.IsDir() {
+ var pathsToRemove []string
+ w.pmut.Lock()
+ for _, wpath := range w.paths {
+ wdir, _ := filepath.Split(wpath)
+ if filepath.Clean(wdir) == filepath.Clean(name) {
+ w.ewmut.Lock()
+ if !w.externalWatches[wpath] {
+ pathsToRemove = append(pathsToRemove, wpath)
+ }
+ w.ewmut.Unlock()
+ }
+ }
+ w.pmut.Unlock()
+ for _, name := range pathsToRemove {
+ // Since these are internal, not much sense in propagating error
+ // to the user, as that will just confuse them with an error about
+ // a path they did not explicitly watch themselves.
+ w.Remove(name)
+ }
+ }
+
+ return nil
+}
+
+const (
+ // Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
+ noteAllEvents = syscall.NOTE_DELETE | syscall.NOTE_WRITE | syscall.NOTE_ATTRIB | syscall.NOTE_RENAME
+
+ // Block for 100 ms on each call to kevent
+ keventWaitTime = 100e6
+)
+
// addWatch adds path to the watched file set.
// The flags are interpreted as described in kevent(2).
func (w *Watcher) addWatch(path string, flags uint32) error {
return nil
}
-// Add starts watching the named file or directory (non-recursively).
-func (w *Watcher) Add(name string) error {
- w.ewmut.Lock()
- w.externalWatches[name] = true
- w.ewmut.Unlock()
- return w.addWatch(name, noteAllEvents)
-}
-
-// Remove stops watching the the named file or directory (non-recursively).
-func (w *Watcher) Remove(name string) error {
- name = filepath.Clean(name)
- w.wmut.Lock()
- watchfd, ok := w.watches[name]
- w.wmut.Unlock()
- if !ok {
- return fmt.Errorf("can't remove non-existent kevent watch for: %s", name)
- }
- var kbuf [1]syscall.Kevent_t
- watchEntry := &kbuf[0]
- syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_DELETE)
- entryFlags := watchEntry.Flags
- success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil)
- if success == -1 {
- return os.NewSyscallError("kevent_rm_watch", errno)
- } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR {
- return errors.New("kevent rm error")
- }
- syscall.Close(watchfd)
- w.wmut.Lock()
- delete(w.watches, name)
- w.wmut.Unlock()
- w.enmut.Lock()
- delete(w.enFlags, name)
- w.enmut.Unlock()
- w.pmut.Lock()
- delete(w.paths, watchfd)
- fInfo := w.finfo[watchfd]
- delete(w.finfo, watchfd)
- w.pmut.Unlock()
-
- // Find all watched paths that are in this directory that are not external.
- if fInfo.IsDir() {
- var pathsToRemove []string
- w.pmut.Lock()
- for _, wpath := range w.paths {
- wdir, _ := filepath.Split(wpath)
- if filepath.Clean(wdir) == filepath.Clean(name) {
- w.ewmut.Lock()
- if !w.externalWatches[wpath] {
- pathsToRemove = append(pathsToRemove, wpath)
- }
- w.ewmut.Unlock()
- }
- }
- w.pmut.Unlock()
- for _, name := range pathsToRemove {
- // Since these are internal, not much sense in propagating error
- // to the user, as that will just confuse them with an error about
- // a path they did not explicitly watch themselves.
- w.Remove(name)
- }
- }
-
- return nil
-}
-
// readEvents reads from the kqueue file descriptor, converts the
// received events into Event objects and sends them via the Events channel
func (w *Watcher) readEvents() {
}
}
+// newEvent returns an platform-independent Event based on kqueue Fflags.
+func newEvent(name string, mask uint32, create bool) Event {
+ e := Event{Name: name}
+ if create {
+ e.Op |= Create
+ }
+ if mask&syscall.NOTE_DELETE == syscall.NOTE_DELETE {
+ e.Op |= Remove
+ }
+ if mask&syscall.NOTE_WRITE == syscall.NOTE_WRITE {
+ e.Op |= Write
+ }
+ if mask&syscall.NOTE_RENAME == syscall.NOTE_RENAME {
+ e.Op |= Rename
+ }
+ if mask&syscall.NOTE_ATTRIB == syscall.NOTE_ATTRIB {
+ e.Op |= Chmod
+ }
+ return e
+}
+
func (w *Watcher) watchDirectoryFiles(dirPath string) error {
// Get all files
files, err := ioutil.ReadDir(dirPath)
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin
-
package fsnotify
import "syscall"
"unsafe"
)
+// 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
+}
+
+// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
+func NewWatcher() (*Watcher, error) {
+ port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
+ if e != nil {
+ return nil, os.NewSyscallError("CreateIoCompletionPort", e)
+ }
+ w := &Watcher{
+ port: port,
+ watches: make(watchMap),
+ input: make(chan *input, 1),
+ Events: make(chan Event, 50),
+ Errors: make(chan error),
+ quit: make(chan chan<- error, 1),
+ }
+ go w.readEvents()
+ return w, nil
+}
+
+// Close removes all watches and closes the events channel.
+func (w *Watcher) Close() error {
+ if w.isClosed {
+ return nil
+ }
+ w.isClosed = true
+
+ // Send "quit" message to the reader goroutine
+ ch := make(chan error)
+ w.quit <- ch
+ if err := w.wakeupReader(); err != nil {
+ return err
+ }
+ return <-ch
+}
+
+// Add starts watching the named file or directory (non-recursively).
+func (w *Watcher) Add(name string) error {
+ if w.isClosed {
+ return errors.New("watcher already closed")
+ }
+ in := &input{
+ op: opAddWatch,
+ path: filepath.Clean(name),
+ flags: sys_FS_ALL_EVENTS,
+ reply: make(chan error),
+ }
+ w.input <- in
+ if err := w.wakeupReader(); err != nil {
+ return err
+ }
+ return <-in.reply
+}
+
+// Remove stops watching the the named file or directory (non-recursively).
+func (w *Watcher) Remove(name string) error {
+ in := &input{
+ op: opRemoveWatch,
+ path: filepath.Clean(name),
+ reply: make(chan error),
+ }
+ w.input <- in
+ if err := w.wakeupReader(); err != nil {
+ return err
+ }
+ return <-in.reply
+}
+
const (
// Options for AddWatch
sys_FS_ONESHOT = 0x80000000
type indexMap map[uint64]*watch
type watchMap map[uint32]indexMap
-// 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
-}
-
-// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
-func NewWatcher() (*Watcher, error) {
- port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
- if e != nil {
- return nil, os.NewSyscallError("CreateIoCompletionPort", e)
- }
- w := &Watcher{
- port: port,
- watches: make(watchMap),
- input: make(chan *input, 1),
- Events: make(chan Event, 50),
- Errors: make(chan error),
- quit: make(chan chan<- error, 1),
- }
- go w.readEvents()
- return w, nil
-}
-
-// Close removes all watches and closes the events channel.
-func (w *Watcher) Close() error {
- if w.isClosed {
- return nil
- }
- w.isClosed = true
-
- // Send "quit" message to the reader goroutine
- ch := make(chan error)
- w.quit <- ch
- if err := w.wakeupReader(); err != nil {
- return err
- }
- return <-ch
-}
-
-// Add starts watching the named file or directory (non-recursively).
-func (w *Watcher) Add(name string) error {
- if w.isClosed {
- return errors.New("watcher already closed")
- }
- in := &input{
- op: opAddWatch,
- path: filepath.Clean(name),
- flags: sys_FS_ALL_EVENTS,
- reply: make(chan error),
- }
- w.input <- in
- if err := w.wakeupReader(); err != nil {
- return err
- }
- return <-in.reply
-}
-
-// Remove stops watching the the named file or directory (non-recursively).
-func (w *Watcher) Remove(name string) error {
- in := &input{
- op: opRemoveWatch,
- path: filepath.Clean(name),
- reply: make(chan error),
- }
- w.input <- in
- if err := w.wakeupReader(); err != nil {
- return err
- }
- return <-in.reply
-}
-
func (w *Watcher) wakeupReader() error {
e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
if e != nil {