-
Notifications
You must be signed in to change notification settings - Fork 129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Some fixes regarding unwatching and rewatching a path #129
Changes from all commits
ca15b52
7321520
07c566e
cea90c3
de41129
1687b00
40b317d
734d890
9c33e90
a55fe76
e2ec277
46fab92
a65f5f9
9a90a47
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -358,7 +358,7 @@ func (i *inotify) Unwatch(path string) (err error) { | |
return errors.New("notify: path " + path + " is already watched") | ||
} | ||
fd := atomic.LoadInt32(&i.fd) | ||
if _, err = unix.InotifyRmWatch(int(fd), uint32(iwd)); err != nil { | ||
if err = removeInotifyWatch(fd, iwd); err != nil { | ||
return | ||
} | ||
i.Lock() | ||
|
@@ -378,7 +378,7 @@ func (i *inotify) Close() (err error) { | |
return nil | ||
} | ||
for iwd := range i.m { | ||
if _, e := unix.InotifyRmWatch(int(i.fd), uint32(iwd)); e != nil && err == nil { | ||
if e := removeInotifyWatch(i.fd, iwd); e != nil && err == nil { | ||
err = e | ||
} | ||
delete(i.m, iwd) | ||
|
@@ -395,3 +395,11 @@ func (i *inotify) Close() (err error) { | |
} | ||
return | ||
} | ||
|
||
// if path was removed, notify already removed the watch and returns EINVAL error | ||
func removeInotifyWatch(fd int32, iwd int32) (err error) { | ||
if _, err = unix.InotifyRmWatch(int(fd), uint32(iwd)); err != nil && err != unix.EINVAL { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
return | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -284,16 +284,18 @@ func (r *readdcw) watch(path string, event Event, recursive bool) (err error) { | |
return | ||
} | ||
r.Lock() | ||
defer r.Unlock() | ||
if wd, ok = r.m[path]; ok { | ||
r.Unlock() | ||
dbgprint("watch: exists already") | ||
return | ||
} | ||
if wd, err = newWatched(r.cph, uint32(event), recursive, path); err != nil { | ||
r.Unlock() | ||
return | ||
} | ||
r.m[path] = wd | ||
r.Unlock() | ||
dbgprint("watch: new watch added") | ||
} else { | ||
dbgprint("watch: exists already") | ||
} | ||
return nil | ||
} | ||
|
@@ -337,33 +339,32 @@ func (r *readdcw) loop() { | |
continue | ||
} | ||
overEx := (*overlappedEx)(unsafe.Pointer(overlapped)) | ||
if n == 0 { | ||
r.loopstate(overEx) | ||
} else { | ||
if n != 0 { | ||
r.loopevent(n, overEx) | ||
if err = overEx.parent.readDirChanges(); err != nil { | ||
// TODO: error handling | ||
} | ||
} | ||
r.loopstate(overEx) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. /cc @ppknap There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LGTM 👍 |
||
} | ||
} | ||
|
||
// TODO(pknap) : doc | ||
func (r *readdcw) loopstate(overEx *overlappedEx) { | ||
filter := atomic.LoadUint32(&overEx.parent.parent.filter) | ||
r.Lock() | ||
defer r.Unlock() | ||
filter := overEx.parent.parent.filter | ||
if filter&onlyMachineStates == 0 { | ||
return | ||
} | ||
if overEx.parent.parent.count--; overEx.parent.parent.count == 0 { | ||
switch filter & onlyMachineStates { | ||
case stateRewatch: | ||
r.Lock() | ||
dbgprint("loopstate rewatch") | ||
overEx.parent.parent.recreate(r.cph) | ||
r.Unlock() | ||
case stateUnwatch: | ||
r.Lock() | ||
dbgprint("loopstate unwatch") | ||
delete(r.m, syscall.UTF16ToString(overEx.parent.pathw)) | ||
r.Unlock() | ||
case stateCPClose: | ||
default: | ||
panic(`notify: windows loopstate logic error`) | ||
|
@@ -450,8 +451,8 @@ func (r *readdcw) rewatch(path string, oldevent, newevent uint32, recursive bool | |
} | ||
var wd *watched | ||
r.Lock() | ||
if wd, err = r.nonStateWatched(path); err != nil { | ||
r.Unlock() | ||
defer r.Unlock() | ||
if wd, err = r.nonStateWatchedLocked(path); err != nil { | ||
return | ||
} | ||
if wd.filter&(onlyNotifyChanges|onlyNGlobalEvents) != oldevent { | ||
|
@@ -462,21 +463,19 @@ func (r *readdcw) rewatch(path string, oldevent, newevent uint32, recursive bool | |
if err = wd.closeHandle(); err != nil { | ||
wd.filter = oldevent | ||
wd.recursive = recursive | ||
r.Unlock() | ||
return | ||
} | ||
r.Unlock() | ||
return | ||
} | ||
|
||
// TODO : pknap | ||
func (r *readdcw) nonStateWatched(path string) (wd *watched, err error) { | ||
func (r *readdcw) nonStateWatchedLocked(path string) (wd *watched, err error) { | ||
wd, ok := r.m[path] | ||
if !ok || wd == nil { | ||
err = errors.New(`notify: ` + path + ` path is unwatched`) | ||
return | ||
} | ||
if filter := atomic.LoadUint32(&wd.filter); filter&onlyMachineStates != 0 { | ||
if wd.filter&onlyMachineStates != 0 { | ||
err = errors.New(`notify: another re/unwatching operation in progress`) | ||
return | ||
} | ||
|
@@ -497,17 +496,26 @@ func (r *readdcw) RecursiveUnwatch(path string) error { | |
func (r *readdcw) unwatch(path string) (err error) { | ||
var wd *watched | ||
r.Lock() | ||
if wd, err = r.nonStateWatched(path); err != nil { | ||
r.Unlock() | ||
defer r.Unlock() | ||
if wd, err = r.nonStateWatchedLocked(path); err != nil { | ||
return | ||
} | ||
wd.filter |= stateUnwatch | ||
if err = wd.closeHandle(); err != nil { | ||
wd.filter &^= stateUnwatch | ||
r.Unlock() | ||
return | ||
} | ||
r.Unlock() | ||
if _, attrErr := syscall.GetFileAttributes(&wd.pathw[0]); attrErr != nil { | ||
for _, g := range wd.digrip { | ||
if g != nil { | ||
dbgprint("unwatch: posting") | ||
if err = syscall.PostQueuedCompletionStatus(r.cph, 0, 0, (*syscall.Overlapped)(unsafe.Pointer(g.ovlapped))); err != nil { | ||
wd.filter &^= stateUnwatch | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. /cc @ppknap There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
return | ||
} | ||
} | ||
} | ||
} | ||
return | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ | |
package notify | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
@@ -56,6 +57,19 @@ type trigger interface { | |
IsStop(n interface{}, err error) bool | ||
} | ||
|
||
// trgWatched is a the base data structure representing watched file/directory. | ||
// The platform specific full data structure (watched) must embed this type. | ||
type trgWatched struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. /cc @pblaszczyk There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think Pawel's review is not needed here since this struct is just a "base" for |
||
// p is a path to watched file/directory. | ||
p string | ||
// fi provides information about watched file/dir. | ||
fi os.FileInfo | ||
// eDir represents events watched directly. | ||
eDir Event | ||
// eNonDir represents events watched indirectly. | ||
eNonDir Event | ||
} | ||
|
||
// encode Event to native representation. Implementation is to be provided by | ||
// platform specific implementation. | ||
var encode func(Event, bool) int64 | ||
|
@@ -117,6 +131,9 @@ func (t *trg) Close() (err error) { | |
dbgprintf("trg: closing native watch failed: %q\n", e) | ||
err = nonil(err, e) | ||
} | ||
if remaining := len(t.pthLkp); remaining != 0 { | ||
err = nonil(err, fmt.Errorf("Not all watches were removed: len(t.pthLkp) == %v", len(t.pthLkp))) | ||
} | ||
t.Unlock() | ||
return | ||
} | ||
|
@@ -175,7 +192,7 @@ func decode(o int64, w Event) (e Event) { | |
func (t *trg) watch(p string, e Event, fi os.FileInfo) error { | ||
if err := t.singlewatch(p, e, dir, fi); err != nil { | ||
if err != errAlreadyWatched { | ||
return nil | ||
return err | ||
} | ||
} | ||
if fi.IsDir() { | ||
|
@@ -361,7 +378,7 @@ func (t *trg) singleunwatch(p string, direct mode) error { | |
} | ||
if w.eNonDir|w.eDir != 0 { | ||
mod := dir | ||
if w.eNonDir == 0 { | ||
if w.eNonDir != 0 { | ||
mod = ndir | ||
} | ||
if err := t.singlewatch(p, w.eNonDir|w.eDir, mod, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we can remove this yet, until the functionality is moved to tree.
Morever I think it'd need to stay here because of
fse.watches
- if we put a watch on a dir and a symlink to it,fse.watches
must not hold two watches but only one.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it already in the tree:
https://github.com/rjeczalik/notify/blob/master/tree_recursive.go#L164
Cleanpath subsequently calls canonical on the path and no other watcher implementation uses canonical.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is, but
fse.watches
maintains its own path -> watches mapping, if we get rid of canonical, we may have duplicated watches.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't understand it: The paths in
fse.watches
that were previously passed through canonical are those given as arguments to the exported (Recursive-)Re-/Watch functions, which are called from the tree, where the paths were already canonicalized in the tree - how else can duplicated paths enterfse.watches
?Other watchers maintains such a map of paths to structs as well, but don't canonicalize paths (e.g. readdcw in
r.m
, trigger inpthLkp
).Also the new tests fail when
canonical
is reintroduced, as it won't remove a non-existing path, becausecanonical
stats the path and thus fails.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, I take back my comment about duplicates - canonical is not needed here.
Good point.