Skip to content
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

Fixes for non-recursive tree when dirs are deleted (fixes #200) #201

Merged
merged 4 commits into from Jun 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
@@ -1,3 +1,5 @@
module github.com/rjeczalik/notify

go 1.11

require golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7
16 changes: 8 additions & 8 deletions node.go
Expand Up @@ -49,7 +49,7 @@ func (nd node) addchild(name, base string) node {
}

func (nd node) Add(name string) node {
i := indexbase(nd.Name, name)
i := indexrel(nd.Name, name)
if i == -1 {
return node{}
}
Expand Down Expand Up @@ -93,7 +93,7 @@ Traverse:
}

func (nd node) Get(name string) (node, error) {
i := indexbase(nd.Name, name)
i := indexrel(nd.Name, name)
if i == -1 {
return node{}, errnotexist(name)
}
Expand All @@ -111,7 +111,7 @@ func (nd node) Get(name string) (node, error) {
}

func (nd node) Del(name string) error {
i := indexbase(nd.Name, name)
i := indexrel(nd.Name, name)
if i == -1 {
return errnotexist(name)
}
Expand All @@ -122,13 +122,13 @@ func (nd node) Del(name string) error {
return errnotexist(name[:i+j])
}
stack = append(stack, nd)
i += j + 1
}
if nd, ok = nd.Child[name[i:]]; !ok {
if _, ok = nd.Child[name[i:]]; !ok {
return errnotexist(name)
}
nd.Child = nil
nd.Watch = nil
for name, i = base(nd.Name), len(stack); i != 0; name, i = base(nd.Name), i-1 {
delete(nd.Child, name[i:])
for name, i = name[i:], len(stack); i != 0; name, i = base(nd.Name), i-1 {
nd = stack[i-1]
if nd := nd.Child[name]; len(nd.Watch) > 1 || len(nd.Child) != 0 {
break
Expand Down Expand Up @@ -167,7 +167,7 @@ Traverse:
}

func (nd node) WalkPath(name string, fn walkPathFunc) error {
i := indexbase(nd.Name, name)
i := indexrel(nd.Name, name)
if i == -1 {
return errnotexist(name)
}
Expand Down
62 changes: 62 additions & 0 deletions notify_test.go
Expand Up @@ -147,6 +147,68 @@ func TestRenameInRoot(t *testing.T) {
}
}

func TestRecreated(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "notify_test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)

dir := filepath.Join(tmpDir, "folder")
file := filepath.Join(dir, "file")

// Start watching
eventChan := make(chan EventInfo, 1000)
mustT(t, Watch(tmpDir+"/...", eventChan, All))
defer Stop(eventChan)

recreateFolder := func() {
// Give the sync some time to process events
_ = os.RemoveAll(dir)
mustT(t, os.Mkdir(dir, 0777))
time.Sleep(100 * time.Millisecond)

// Create a file
mustT(t, ioutil.WriteFile(file, []byte("abc"), 0666))
}
timeout := time.After(5 * time.Second)
checkCreated := func() {
for {
select {
case ev := <-eventChan:
t.Log(ev.Path(), ev.Event())
if ev.Path() == file && ev.Event() == Create {
return
}
case <-timeout:
t.Fatal("timed out before receiving event")
}
}
}

// 1. Create a folder and a file within it
// This will create a node in the internal tree for the subfolder test/folder
// Will create a new inotify watch for the folder
t.Log("######## First ########")
recreateFolder()
checkCreated()

// 2. Create a folder and a file within it again
// This will set the events for the subfolder test/folder in the internal tree
// Will create a new inotify watch for the folder because events differ
t.Log("######## Second ########")
recreateFolder()
checkCreated()

// 3. Create a folder and a file within it yet again
// This time no new inotify watch will be created, because the events
// and node already exist in the internal tree and all subsequent events
// are lost, hence there is no event for the created file here anymore
t.Log("######## Third ########")
recreateFolder()
checkCreated()
}

func mustT(t testing.TB, err error) {
t.Helper()
if err != nil {
Expand Down
23 changes: 20 additions & 3 deletions tree_nonrecursive.go
Expand Up @@ -65,7 +65,7 @@ func (t *nonrecursiveTree) dispatch(c <-chan EventInfo) {
}
t.rw.RUnlock()
// If the event describes newly leaf directory created within
if !isrec || ei.Event() != Create {
if !isrec || ei.Event()&(Create|Remove) == 0 {
return
}
if ok, err := ei.(isDirer).isDir(); !ok || err != nil {
Expand All @@ -79,9 +79,23 @@ func (t *nonrecursiveTree) dispatch(c <-chan EventInfo) {
// internal TODO(rjeczalik)
func (t *nonrecursiveTree) internal(rec <-chan EventInfo) {
for ei := range rec {
t.rw.Lock()
if ei.Event() == Remove {
nd, err := t.root.Get(ei.Path())
if err != nil {
t.rw.Unlock()
continue
}
t.walkWatchpoint(nd, func(_ Event, nd node) error {
t.w.Unwatch(nd.Name)
return nil
})
t.root.Del(ei.Path())
t.rw.Unlock()
continue
}
var nd node
var eset = internal
t.rw.Lock()
t.root.WalkPath(ei.Path(), func(it node, _ bool) error {
if e := it.Watch[t.rec]; e != 0 && e > eset {
eset = e
Expand All @@ -93,7 +107,10 @@ func (t *nonrecursiveTree) internal(rec <-chan EventInfo) {
t.rw.Unlock()
continue
}
err := nd.Add(ei.Path()).AddDir(t.recFunc(eset))
if ei.Path() != nd.Name {
nd = nd.Add(ei.Path())
}
err := nd.AddDir(t.recFunc(eset))
t.rw.Unlock()
if err != nil {
dbgprintf("internal(%p) error: %v", rec, err)
Expand Down
10 changes: 6 additions & 4 deletions util.go
Expand Up @@ -123,10 +123,12 @@ func base(s string) string {
return s
}

func indexbase(root, name string) int {
if n, m := len(root), len(name); m >= n && name[:n] == root &&
(n == m || name[n] == os.PathSeparator) {
return min(n+1, m)
// indexrel returns the index of the first char of name that is
// below/relative to root. It returns -1 if name is not a child of root.
func indexrel(root, name string) int {
if n, m := len(root), len(name); m > n && name[:n] == root &&
name[n] == os.PathSeparator {
return n + 1
}
return -1
}
Expand Down