Skip to content

Commit

Permalink
global: switch to x/sys/unix
Browse files Browse the repository at this point in the history
The syscall package is locked since 1.4[1], all new changes are in x/sys[2]
The fixes for epoll/arm64 are going to be loaded to x/sys/unix. This
commit is straightforward search/replace with a couple of hand edits.

This is needed for fixing #130

[1] https://golang.org/s/go1.4-syscall
[2] https://godoc.org/golang.org/x/sys/unix

closes #135
  • Loading branch information
Riku Voipio authored and nathany committed Apr 20, 2016
1 parent 836bfd9 commit 30411db
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 101 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v1.3.0 / 2016-04-19

* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)

## v1.2.10 / 2016-03-02

* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj)
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

[![GoDoc](https://godoc.org/github.com/fsnotify/fsnotify?status.svg)](https://godoc.org/github.com/fsnotify/fsnotify) [![Go Report Card](https://goreportcard.com/badge/github.com/fsnotify/fsnotify)](https://goreportcard.com/report/github.com/fsnotify/fsnotify) [![Coverage](http://gocover.io/_badge/github.com/fsnotify/fsnotify)](http://gocover.io/github.com/fsnotify/fsnotify)

Go 1.3+ required.
fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running:

```console
go get -u golang.org/x/sys/...
```

Cross platform: Windows, Linux, BSD and OS X.

Expand All @@ -27,7 +31,7 @@ fsnotify is a fork of [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsno

All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number.

Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project.
Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`.

## Contributing

Expand Down
57 changes: 29 additions & 28 deletions inotify.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import (
"path/filepath"
"strings"
"sync"
"syscall"
"unsafe"

"golang.org/x/sys/unix"
)

// Watcher watches a set of files, delivering events to a channel.
Expand All @@ -35,14 +36,14 @@ type Watcher struct {
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
func NewWatcher() (*Watcher, error) {
// Create inotify fd
fd, errno := syscall.InotifyInit()
fd, errno := unix.InotifyInit()
if fd == -1 {
return nil, errno
}
// Create epoll
poller, err := newFdPoller(fd)
if err != nil {
syscall.Close(fd)
unix.Close(fd)
return nil, err
}
w := &Watcher{
Expand Down Expand Up @@ -95,9 +96,9 @@ func (w *Watcher) Add(name string) error {
return errors.New("inotify instance already closed")
}

const agnosticEvents = syscall.IN_MOVED_TO | syscall.IN_MOVED_FROM |
syscall.IN_CREATE | syscall.IN_ATTRIB | syscall.IN_MODIFY |
syscall.IN_MOVE_SELF | syscall.IN_DELETE | syscall.IN_DELETE_SELF
const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF

var flags uint32 = agnosticEvents

Expand All @@ -106,9 +107,9 @@ func (w *Watcher) Add(name string) error {
w.mu.Unlock()
if found {
watchEntry.flags |= flags
flags |= syscall.IN_MASK_ADD
flags |= unix.IN_MASK_ADD
}
wd, errno := syscall.InotifyAddWatch(w.fd, name, flags)
wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
if wd == -1 {
return errno
}
Expand Down Expand Up @@ -140,7 +141,7 @@ func (w *Watcher) Remove(name string) error {
// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
// so that EINVAL means that the wd is being rm_watch()ed or its file removed
// by another thread and we have not received IN_IGNORE event.
success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
if success == -1 {
// TODO: Perhaps it's not helpful to return an error here in every case.
// the only two possible errors are:
Expand Down Expand Up @@ -170,16 +171,16 @@ type watch struct {
// received events into Event objects and sends them via the Events channel
func (w *Watcher) readEvents() {
var (
buf [syscall.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
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 syscall.Close(w.fd)
defer unix.Close(w.fd)
defer w.poller.close()

for {
Expand All @@ -202,20 +203,20 @@ func (w *Watcher) readEvents() {
continue
}

n, errno = syscall.Read(w.fd, buf[:])
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 == syscall.EINTR {
if errno == unix.EINTR {
continue
}

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

if n < syscall.SizeofInotifyEvent {
if n < unix.SizeofInotifyEvent {
var err error
if n == 0 {
// If EOF is received. This should really never happen.
Expand All @@ -238,9 +239,9 @@ func (w *Watcher) readEvents() {
var offset uint32
// We don't know how many events we just read into the buffer
// While the offset points to at least one whole event...
for offset <= uint32(n-syscall.SizeofInotifyEvent) {
for offset <= uint32(n-unix.SizeofInotifyEvent) {
// Point "raw" to the event in the buffer
raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))

mask := uint32(raw.Mask)
nameLen := uint32(raw.Len)
Expand All @@ -253,7 +254,7 @@ func (w *Watcher) readEvents() {
w.mu.Unlock()
if nameLen > 0 {
// Point "bytes" at the first byte of the filename
bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))
// The filename is padded with NULL bytes. TrimRight() gets rid of those.
name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
}
Expand All @@ -270,7 +271,7 @@ func (w *Watcher) readEvents() {
}

// Move to the next event in the buffer
offset += syscall.SizeofInotifyEvent + nameLen
offset += unix.SizeofInotifyEvent + nameLen
}
}
}
Expand All @@ -280,7 +281,7 @@ func (w *Watcher) readEvents() {
// against files that do not exist.
func (e *Event) ignoreLinux(w *Watcher, wd int32, mask uint32) bool {
// Ignore anything the inotify API says to ignore
if mask&syscall.IN_IGNORED == syscall.IN_IGNORED {
if mask&unix.IN_IGNORED == unix.IN_IGNORED {
w.mu.Lock()
defer w.mu.Unlock()
name := w.paths[int(wd)]
Expand All @@ -305,19 +306,19 @@ func (e *Event) ignoreLinux(w *Watcher, wd int32, mask uint32) bool {
// newEvent returns an platform-independent Event based on an inotify mask.
func newEvent(name string, mask uint32) Event {
e := Event{Name: name}
if mask&syscall.IN_CREATE == syscall.IN_CREATE || mask&syscall.IN_MOVED_TO == syscall.IN_MOVED_TO {
if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO {
e.Op |= Create
}
if mask&syscall.IN_DELETE_SELF == syscall.IN_DELETE_SELF || mask&syscall.IN_DELETE == syscall.IN_DELETE {
if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE {
e.Op |= Remove
}
if mask&syscall.IN_MODIFY == syscall.IN_MODIFY {
if mask&unix.IN_MODIFY == unix.IN_MODIFY {
e.Op |= Write
}
if mask&syscall.IN_MOVE_SELF == syscall.IN_MOVE_SELF || mask&syscall.IN_MOVED_FROM == syscall.IN_MOVED_FROM {
if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
e.Op |= Rename
}
if mask&syscall.IN_ATTRIB == syscall.IN_ATTRIB {
if mask&unix.IN_ATTRIB == unix.IN_ATTRIB {
e.Op |= Chmod
}
return e
Expand Down
53 changes: 27 additions & 26 deletions inotify_poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ package fsnotify

import (
"errors"
"syscall"

"golang.org/x/sys/unix"
)

type fdPoller struct {
Expand Down Expand Up @@ -39,32 +40,32 @@ func newFdPoller(fd int) (*fdPoller, error) {
poller.fd = fd

// Create epoll fd
poller.epfd, errno = syscall.EpollCreate1(0)
poller.epfd, errno = unix.EpollCreate1(0)
if poller.epfd == -1 {
return nil, errno
}
// Create pipe; pipe[0] is the read end, pipe[1] the write end.
errno = syscall.Pipe2(poller.pipe[:], syscall.O_NONBLOCK)
errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK)
if errno != nil {
return nil, errno
}

// Register inotify fd with epoll
event := syscall.EpollEvent{
event := unix.EpollEvent{
Fd: int32(poller.fd),
Events: syscall.EPOLLIN,
Events: unix.EPOLLIN,
}
errno = syscall.EpollCtl(poller.epfd, syscall.EPOLL_CTL_ADD, poller.fd, &event)
errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event)
if errno != nil {
return nil, errno
}

// Register pipe fd with epoll
event = syscall.EpollEvent{
event = unix.EpollEvent{
Fd: int32(poller.pipe[0]),
Events: syscall.EPOLLIN,
Events: unix.EPOLLIN,
}
errno = syscall.EpollCtl(poller.epfd, syscall.EPOLL_CTL_ADD, poller.pipe[0], &event)
errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event)
if errno != nil {
return nil, errno
}
Expand All @@ -80,11 +81,11 @@ func (poller *fdPoller) wait() (bool, error) {
// I don't know whether epoll_wait returns the number of events returned,
// or the total number of events ready.
// I decided to catch both by making the buffer one larger than the maximum.
events := make([]syscall.EpollEvent, 7)
events := make([]unix.EpollEvent, 7)
for {
n, errno := syscall.EpollWait(poller.epfd, events, -1)
n, errno := unix.EpollWait(poller.epfd, events, -1)
if n == -1 {
if errno == syscall.EINTR {
if errno == unix.EINTR {
continue
}
return false, errno
Expand All @@ -103,31 +104,31 @@ func (poller *fdPoller) wait() (bool, error) {
epollin := false
for _, event := range ready {
if event.Fd == int32(poller.fd) {
if event.Events&syscall.EPOLLHUP != 0 {
if event.Events&unix.EPOLLHUP != 0 {
// This should not happen, but if it does, treat it as a wakeup.
epollhup = true
}
if event.Events&syscall.EPOLLERR != 0 {
if event.Events&unix.EPOLLERR != 0 {
// If an error is waiting on the file descriptor, we should pretend
// something is ready to read, and let syscall.Read pick up the error.
// something is ready to read, and let unix.Read pick up the error.
epollerr = true
}
if event.Events&syscall.EPOLLIN != 0 {
if event.Events&unix.EPOLLIN != 0 {
// There is data to read.
epollin = true
}
}
if event.Fd == int32(poller.pipe[0]) {
if event.Events&syscall.EPOLLHUP != 0 {
if event.Events&unix.EPOLLHUP != 0 {
// Write pipe descriptor was closed, by us. This means we're closing down the
// watcher, and we should wake up.
}
if event.Events&syscall.EPOLLERR != 0 {
if event.Events&unix.EPOLLERR != 0 {
// If an error is waiting on the pipe file descriptor.
// This is an absolute mystery, and should never ever happen.
return false, errors.New("Error on the pipe descriptor.")
}
if event.Events&syscall.EPOLLIN != 0 {
if event.Events&unix.EPOLLIN != 0 {
// This is a regular wakeup, so we have to clear the buffer.
err := poller.clearWake()
if err != nil {
Expand All @@ -147,9 +148,9 @@ func (poller *fdPoller) wait() (bool, error) {
// Close the write end of the poller.
func (poller *fdPoller) wake() error {
buf := make([]byte, 1)
n, errno := syscall.Write(poller.pipe[1], buf)
n, errno := unix.Write(poller.pipe[1], buf)
if n == -1 {
if errno == syscall.EAGAIN {
if errno == unix.EAGAIN {
// Buffer is full, poller will wake.
return nil
}
Expand All @@ -161,9 +162,9 @@ func (poller *fdPoller) wake() error {
func (poller *fdPoller) clearWake() error {
// You have to be woken up a LOT in order to get to 100!
buf := make([]byte, 100)
n, errno := syscall.Read(poller.pipe[0], buf)
n, errno := unix.Read(poller.pipe[0], buf)
if n == -1 {
if errno == syscall.EAGAIN {
if errno == unix.EAGAIN {
// Buffer is empty, someone else cleared our wake.
return nil
}
Expand All @@ -175,12 +176,12 @@ func (poller *fdPoller) clearWake() error {
// Close all poller file descriptors, but not the one passed to it.
func (poller *fdPoller) close() {
if poller.pipe[1] != -1 {
syscall.Close(poller.pipe[1])
unix.Close(poller.pipe[1])
}
if poller.pipe[0] != -1 {
syscall.Close(poller.pipe[0])
unix.Close(poller.pipe[0])
}
if poller.epfd != -1 {
syscall.Close(poller.epfd)
unix.Close(poller.epfd)
}
}

0 comments on commit 30411db

Please sign in to comment.