Skip to content

Commit

Permalink
Replace Use of Kthread-blocking Epoll with Poller Read, Remove Per-Ev…
Browse files Browse the repository at this point in the history
…ent LStats on Linux #433 (#434)

* Replaced use of raw epoll with netpoller read

* Remove Debian 6 Vagrant test; it's in #469 now

* Added ignoreLinux lstats back in

* Update test

Co-authored-by: Martin Tournoij <martin@arp242.net>
  • Loading branch information
horahoradev and arp242 committed Jul 24, 2022
1 parent 4b8b298 commit c4e64e4
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 471 deletions.
82 changes: 32 additions & 50 deletions inotify.go
Expand Up @@ -22,39 +22,36 @@ import (

// Watcher watches a set of files, delivering events to a channel.
type Watcher struct {
Events chan Event
Errors chan error
mu sync.Mutex // Map access
fd int
poller *fdPoller
watches map[string]*watch // Map of inotify watches (key: path)
paths map[int]string // Map of watched paths (key: watch descriptor)
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
doneResp chan struct{} // Channel to respond to Close
fd int // https://github.com/golang/go/issues/26439 can't call .Fd() on os.FIle or Read will no longer return on Close()
Events chan Event
Errors chan error
mu sync.Mutex // Map access
inotifyFile *os.File
watches map[string]*watch // Map of inotify watches (key: path)
paths map[int]string // Map of watched paths (key: watch descriptor)
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
doneResp chan struct{} // Channel to respond to Close
}

// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
func NewWatcher() (*Watcher, error) {
// Create inotify fd
fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC)
// Need to set the FD to nonblocking mode in order for SetDeadline methods to work
// Otherwise, blocking i/o operations won't terminate on close
fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK)
if fd == -1 {
return nil, errno
}
// Create epoll
poller, err := newFdPoller(fd)
if err != nil {
unix.Close(fd)
return nil, err
}

w := &Watcher{
fd: fd,
poller: poller,
watches: make(map[string]*watch),
paths: make(map[int]string),
Events: make(chan Event),
Errors: make(chan error),
done: make(chan struct{}),
doneResp: make(chan struct{}),
fd: fd,
inotifyFile: os.NewFile(uintptr(fd), ""),
watches: make(map[string]*watch),
paths: make(map[int]string),
Events: make(chan Event),
Errors: make(chan error),
done: make(chan struct{}),
doneResp: make(chan struct{}),
}

go w.readEvents()
Expand Down Expand Up @@ -82,8 +79,11 @@ func (w *Watcher) Close() error {
close(w.done)
w.mu.Unlock()

// Wake up goroutine
w.poller.wake()
// Causes any blocking reads to return with an error, provided the file still supports deadline operations
err := w.inotifyFile.Close()
if err != nil {
return err
}

// Wait for goroutine to close
<-w.doneResp
Expand Down Expand Up @@ -189,50 +189,32 @@ type watch struct {
func (w *Watcher) readEvents() {
var (
buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
n int // Number of bytes read with read()
errno error // Syscall errno
ok bool // For poller.wait
)

defer close(w.doneResp)
defer close(w.Errors)
defer close(w.Events)
defer unix.Close(w.fd)
defer w.poller.close()

for {
// See if we have been closed.
if w.isClosed() {
return
}

ok, errno = w.poller.wait()
if errno != nil {
n, err := w.inotifyFile.Read(buf[:])
switch {
case errors.Unwrap(err) == os.ErrClosed:
return
case err != nil:
select {
case w.Errors <- errno:
case w.Errors <- err:
case <-w.done:
return
}
continue
}

if !ok {
continue
}

n, errno = unix.Read(w.fd, buf[:])
// If a signal interrupted execution, see if we've been asked to close, and try again.
// http://man7.org/linux/man-pages/man7/signal.7.html :
// "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable"
if errno == unix.EINTR {
continue
}

// unix.Read might have been woken up by Close. If so, we're done.
if w.isClosed() {
return
}

if n < unix.SizeofInotifyEvent {
var err error
if n == 0 {
Expand Down
187 changes: 0 additions & 187 deletions inotify_poller.go

This file was deleted.

0 comments on commit c4e64e4

Please sign in to comment.