Commit e5323420 authored by Kirill Smelkov's avatar Kirill Smelkov

X stub fsnotify-compatible module that works via polling

fsnotify uses unsafe and we are digging GC crash trying to avoid Cgo and
unsafe.
parent 73b9c447
...@@ -78,7 +78,8 @@ import ( ...@@ -78,7 +78,8 @@ import (
"lab.nexedi.com/kirr/go123/mem" "lab.nexedi.com/kirr/go123/mem"
"lab.nexedi.com/kirr/go123/xerr" "lab.nexedi.com/kirr/go123/xerr"
"github.com/fsnotify/fsnotify" //"github.com/fsnotify/fsnotify"
"lab.nexedi.com/kirr/neo/go/zodb/storage/fs1/internal/fsnotify-stub/fsnotify"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
......
// Package fsnotify implements github.com/fsnotify/fsnotify interface via polling.
package fsnotify
import (
"context"
"errors"
"os"
"time"
"lab.nexedi.com/kirr/go123/xerr"
"lab.nexedi.com/kirr/go123/xsync"
//"fmt"
)
var ErrEventOverflow = errors.New("fsnotify queue overflow")
type Op uint32
const (
Create Op = 1 << iota
Write
Remove
Rename
Chmod
)
type Event struct {
Name string
Op Op
}
type Watcher struct {
Events chan Event
Errors chan error
watchCancel func()
watchGroup *xsync.WorkGroup
}
func NewWatcher() (*Watcher, error) {
ctx, cancel := context.WithCancel(context.Background())
wg := xsync.NewWorkGroup(ctx)
return &Watcher{
Events: make(chan Event), // XXX no buf?
Errors: make(chan error), // XXX no buf?
watchCancel: cancel,
watchGroup: wg,
}, nil
}
func (w *Watcher) Close() error {
//fmt.Printf("watch: Close\n")
w.watchCancel()
err := w.watchGroup.Wait()
close(w.Events)
close(w.Errors)
return err
}
func (w *Watcher) Add(name string) error {
//fmt.Printf("watch: Add %s\n", name)
w.watchGroup.Go(func(ctx context.Context) error {
return w.watch1(ctx, name)
})
return nil
}
func (w *Watcher) watch1(ctx context.Context, name string) (err error) {
defer xerr.Contextf(&err, "watch %s", name)
fiPrev, err := os.Lstat(name)
if err != nil && os.IsNotExist(err) {
err = nil
}
if err != nil {
select {
case <-ctx.Done():
return ctx.Err()
case w.Errors <- err:
return err
}
}
//
for {
if err := ctx.Err(); err != nil {
return err
}
fi, err := os.Lstat(name)
//fmt.Printf("watch %s: -> %v, %v\n", name, fi, err)
if err != nil && os.IsNotExist(err) {
err = nil
}
if err != nil {
select {
case <-ctx.Done():
return ctx.Err()
case w.Errors <- err:
return err
}
}
ev := Event{Name: name}
send := false
switch {
case fiPrev == nil && fi == nil:
// nothing
case fiPrev == nil && fi != nil:
// created
ev.Op = Create
send = true
case fiPrev != nil && fi == nil:
// removed
ev.Op = Remove
send = true
default:
// fiPrev != nil && fi != nil
if fiPrev.ModTime() != fi.ModTime() {
ev.Op = Write
send = true
}
}
if send {
select {
case <-ctx.Done():
return ctx.Err()
case w.Events <- ev:
// ok
}
}
time.Sleep(1*time.Millisecond)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment