Skip to content

Commit

Permalink
Merge pull request #38 from shogo82148/port-434
Browse files Browse the repository at this point in the history
Replace Use of Kthread-blocking Epoll with Poller Read, Remove Per-Event LStats on Linux
  • Loading branch information
shogo82148 committed Mar 6, 2024
2 parents 2deb136 + 1af078c commit e4eebfb
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 471 deletions.
83 changes: 33 additions & 50 deletions inotify.go
Expand Up @@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"sync"
Expand All @@ -21,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 All @@ -78,8 +76,11 @@ func (w *Watcher) Close() error {
// Send 'close' signal to goroutine, and set the Watcher to closed.
close(w.done)

// 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 @@ -185,50 +186,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 e4eebfb

Please sign in to comment.