289b30715d
This commit converts the dependency management from Godeps to the vendor folder, also switching the tool from godep to trash. Since the upstream tool lacks a few features proposed via a few PRs, until those PRs are merged in (if), use github.com/karalabe/trash. You can update dependencies via trash --update. All dependencies have been updated to their latest version. Parts of the build system are reworked to drop old notions of Godeps and invocation of the go vet command so that it doesn't run against the vendor folder, as that will just blow up during vetting. The conversion drops OpenCL (and hence GPU mining support) from ethash and our codebase. The short reasoning is that there's noone to maintain and having opencl libs in our deps messes up builds as go install ./... tries to build them, failing with unsatisfied link errors for the C OpenCL deps. golang.org/x/net/context is not vendored in. We expect it to be fetched by the user (i.e. using go get). To keep ci.go builds reproducible the package is "vendored" in build/_vendor.
397 lines
11 KiB
Go
397 lines
11 KiB
Go
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
|
|
// Use of this source code is governed by the MIT license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// +build linux
|
|
|
|
package notify
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"path/filepath"
|
|
"runtime"
|
|
"sync"
|
|
"sync/atomic"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
// eventBufferSize defines the size of the buffer given to read(2) function. One
|
|
// should not depend on this value, since it was arbitrary chosen and may be
|
|
// changed in the future.
|
|
const eventBufferSize = 64 * (syscall.SizeofInotifyEvent + syscall.PathMax + 1)
|
|
|
|
// consumersCount defines the number of consumers in producer-consumer based
|
|
// implementation. Each consumer is run in a separate goroutine and has read
|
|
// access to watched files map.
|
|
const consumersCount = 2
|
|
|
|
const invalidDescriptor = -1
|
|
|
|
// watched is a pair of file path and inotify mask used as a value in
|
|
// watched files map.
|
|
type watched struct {
|
|
path string
|
|
mask uint32
|
|
}
|
|
|
|
// inotify implements Watcher interface.
|
|
type inotify struct {
|
|
sync.RWMutex // protects inotify.m map
|
|
m map[int32]*watched // watch descriptor to watched object
|
|
fd int32 // inotify file descriptor
|
|
pipefd []int // pipe's read and write descriptors
|
|
epfd int // epoll descriptor
|
|
epes []syscall.EpollEvent // epoll events
|
|
buffer [eventBufferSize]byte // inotify event buffer
|
|
wg sync.WaitGroup // wait group used to close main loop
|
|
c chan<- EventInfo // event dispatcher channel
|
|
}
|
|
|
|
// NewWatcher creates new non-recursive inotify backed by inotify.
|
|
func newWatcher(c chan<- EventInfo) watcher {
|
|
i := &inotify{
|
|
m: make(map[int32]*watched),
|
|
fd: invalidDescriptor,
|
|
pipefd: []int{invalidDescriptor, invalidDescriptor},
|
|
epfd: invalidDescriptor,
|
|
epes: make([]syscall.EpollEvent, 0),
|
|
c: c,
|
|
}
|
|
runtime.SetFinalizer(i, func(i *inotify) {
|
|
i.epollclose()
|
|
if i.fd != invalidDescriptor {
|
|
syscall.Close(int(i.fd))
|
|
}
|
|
})
|
|
return i
|
|
}
|
|
|
|
// Watch implements notify.watcher interface.
|
|
func (i *inotify) Watch(path string, e Event) error {
|
|
return i.watch(path, e)
|
|
}
|
|
|
|
// Rewatch implements notify.watcher interface.
|
|
func (i *inotify) Rewatch(path string, _, newevent Event) error {
|
|
return i.watch(path, newevent)
|
|
}
|
|
|
|
// watch adds a new watcher to the set of watched objects or modifies the existing
|
|
// one. If called for the first time, this function initializes inotify filesystem
|
|
// monitor and starts producer-consumers goroutines.
|
|
func (i *inotify) watch(path string, e Event) (err error) {
|
|
if e&^(All|Event(syscall.IN_ALL_EVENTS)) != 0 {
|
|
return errors.New("notify: unknown event")
|
|
}
|
|
if err = i.lazyinit(); err != nil {
|
|
return
|
|
}
|
|
iwd, err := syscall.InotifyAddWatch(int(i.fd), path, encode(e))
|
|
if err != nil {
|
|
return
|
|
}
|
|
i.RLock()
|
|
wd := i.m[int32(iwd)]
|
|
i.RUnlock()
|
|
if wd == nil {
|
|
i.Lock()
|
|
if i.m[int32(iwd)] == nil {
|
|
i.m[int32(iwd)] = &watched{path: path, mask: uint32(e)}
|
|
}
|
|
i.Unlock()
|
|
} else {
|
|
i.Lock()
|
|
wd.mask = uint32(e)
|
|
i.Unlock()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// lazyinit sets up all required file descriptors and starts 1+consumersCount
|
|
// goroutines. The producer goroutine blocks until file-system notifications
|
|
// occur. Then, all events are read from system buffer and sent to consumer
|
|
// goroutines which construct valid notify events. This method uses
|
|
// Double-Checked Locking optimization.
|
|
func (i *inotify) lazyinit() error {
|
|
if atomic.LoadInt32(&i.fd) == invalidDescriptor {
|
|
i.Lock()
|
|
defer i.Unlock()
|
|
if atomic.LoadInt32(&i.fd) == invalidDescriptor {
|
|
fd, err := syscall.InotifyInit()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
i.fd = int32(fd)
|
|
if err = i.epollinit(); err != nil {
|
|
_, _ = i.epollclose(), syscall.Close(int(fd)) // Ignore errors.
|
|
i.fd = invalidDescriptor
|
|
return err
|
|
}
|
|
esch := make(chan []*event)
|
|
go i.loop(esch)
|
|
i.wg.Add(consumersCount)
|
|
for n := 0; n < consumersCount; n++ {
|
|
go i.send(esch)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// epollinit opens an epoll file descriptor and creates a pipe which will be
|
|
// used to wake up the epoll_wait(2) function. Then, file descriptor associated
|
|
// with inotify event queue and the read end of the pipe are added to epoll set.
|
|
// Note that `fd` member must be set before this function is called.
|
|
func (i *inotify) epollinit() (err error) {
|
|
if i.epfd, err = syscall.EpollCreate1(0); err != nil {
|
|
return
|
|
}
|
|
if err = syscall.Pipe(i.pipefd); err != nil {
|
|
return
|
|
}
|
|
i.epes = []syscall.EpollEvent{
|
|
{Events: syscall.EPOLLIN, Fd: i.fd},
|
|
{Events: syscall.EPOLLIN, Fd: int32(i.pipefd[0])},
|
|
}
|
|
if err = syscall.EpollCtl(i.epfd, syscall.EPOLL_CTL_ADD, int(i.fd), &i.epes[0]); err != nil {
|
|
return
|
|
}
|
|
return syscall.EpollCtl(i.epfd, syscall.EPOLL_CTL_ADD, i.pipefd[0], &i.epes[1])
|
|
}
|
|
|
|
// epollclose closes the file descriptor created by the call to epoll_create(2)
|
|
// and two file descriptors opened by pipe(2) function.
|
|
func (i *inotify) epollclose() (err error) {
|
|
if i.epfd != invalidDescriptor {
|
|
if err = syscall.Close(i.epfd); err == nil {
|
|
i.epfd = invalidDescriptor
|
|
}
|
|
}
|
|
for n, fd := range i.pipefd {
|
|
if fd != invalidDescriptor {
|
|
switch e := syscall.Close(fd); {
|
|
case e != nil && err == nil:
|
|
err = e
|
|
case e == nil:
|
|
i.pipefd[n] = invalidDescriptor
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// loop blocks until either inotify or pipe file descriptor is ready for I/O.
|
|
// All read operations triggered by filesystem notifications are forwarded to
|
|
// one of the event's consumers. If pipe fd became ready, loop function closes
|
|
// all file descriptors opened by lazyinit method and returns afterwards.
|
|
func (i *inotify) loop(esch chan<- []*event) {
|
|
epes := make([]syscall.EpollEvent, 1)
|
|
fd := atomic.LoadInt32(&i.fd)
|
|
for {
|
|
switch _, err := syscall.EpollWait(i.epfd, epes, -1); err {
|
|
case nil:
|
|
switch epes[0].Fd {
|
|
case fd:
|
|
esch <- i.read()
|
|
epes[0].Fd = 0
|
|
case int32(i.pipefd[0]):
|
|
i.Lock()
|
|
defer i.Unlock()
|
|
if err = syscall.Close(int(fd)); err != nil && err != syscall.EINTR {
|
|
panic("notify: close(2) error " + err.Error())
|
|
}
|
|
atomic.StoreInt32(&i.fd, invalidDescriptor)
|
|
if err = i.epollclose(); err != nil && err != syscall.EINTR {
|
|
panic("notify: epollclose error " + err.Error())
|
|
}
|
|
close(esch)
|
|
return
|
|
}
|
|
case syscall.EINTR:
|
|
continue
|
|
default: // We should never reach this line.
|
|
panic("notify: epoll_wait(2) error " + err.Error())
|
|
}
|
|
}
|
|
}
|
|
|
|
// read reads events from an inotify file descriptor. It does not handle errors
|
|
// returned from read(2) function since they are not critical to watcher logic.
|
|
func (i *inotify) read() (es []*event) {
|
|
n, err := syscall.Read(int(i.fd), i.buffer[:])
|
|
if err != nil || n < syscall.SizeofInotifyEvent {
|
|
return
|
|
}
|
|
var sys *syscall.InotifyEvent
|
|
nmin := n - syscall.SizeofInotifyEvent
|
|
for pos, path := 0, ""; pos <= nmin; {
|
|
sys = (*syscall.InotifyEvent)(unsafe.Pointer(&i.buffer[pos]))
|
|
pos += syscall.SizeofInotifyEvent
|
|
if path = ""; sys.Len > 0 {
|
|
endpos := pos + int(sys.Len)
|
|
path = string(bytes.TrimRight(i.buffer[pos:endpos], "\x00"))
|
|
pos = endpos
|
|
}
|
|
es = append(es, &event{
|
|
sys: syscall.InotifyEvent{
|
|
Wd: sys.Wd,
|
|
Mask: sys.Mask,
|
|
Cookie: sys.Cookie,
|
|
},
|
|
path: path,
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
// send is a consumer function which sends events to event dispatcher channel.
|
|
// It is run in a separate goroutine in order to not block loop method when
|
|
// possibly expensive write operations are performed on inotify map.
|
|
func (i *inotify) send(esch <-chan []*event) {
|
|
for es := range esch {
|
|
for _, e := range i.transform(es) {
|
|
if e != nil {
|
|
i.c <- e
|
|
}
|
|
}
|
|
}
|
|
i.wg.Done()
|
|
}
|
|
|
|
// transform prepares events read from inotify file descriptor for sending to
|
|
// user. It removes invalid events and these which are no longer present in
|
|
// inotify map. This method may also split one raw event into two different ones
|
|
// when system-dependent result is required.
|
|
func (i *inotify) transform(es []*event) []*event {
|
|
var multi []*event
|
|
i.RLock()
|
|
for idx, e := range es {
|
|
if e.sys.Mask&(syscall.IN_IGNORED|syscall.IN_Q_OVERFLOW) != 0 {
|
|
es[idx] = nil
|
|
continue
|
|
}
|
|
wd, ok := i.m[e.sys.Wd]
|
|
if !ok || e.sys.Mask&encode(Event(wd.mask)) == 0 {
|
|
es[idx] = nil
|
|
continue
|
|
}
|
|
if e.path == "" {
|
|
e.path = wd.path
|
|
} else {
|
|
e.path = filepath.Join(wd.path, e.path)
|
|
}
|
|
multi = append(multi, decode(Event(wd.mask), e))
|
|
if e.event == 0 {
|
|
es[idx] = nil
|
|
}
|
|
}
|
|
i.RUnlock()
|
|
es = append(es, multi...)
|
|
return es
|
|
}
|
|
|
|
// encode converts notify system-independent events to valid inotify mask
|
|
// which can be passed to inotify_add_watch(2) function.
|
|
func encode(e Event) uint32 {
|
|
if e&Create != 0 {
|
|
e = (e ^ Create) | InCreate | InMovedTo
|
|
}
|
|
if e&Remove != 0 {
|
|
e = (e ^ Remove) | InDelete | InDeleteSelf
|
|
}
|
|
if e&Write != 0 {
|
|
e = (e ^ Write) | InModify
|
|
}
|
|
if e&Rename != 0 {
|
|
e = (e ^ Rename) | InMovedFrom | InMoveSelf
|
|
}
|
|
return uint32(e)
|
|
}
|
|
|
|
// decode uses internally stored mask to distinguish whether system-independent
|
|
// or system-dependent event is requested. The first one is created by modifying
|
|
// `e` argument. decode method sets e.event value to 0 when an event should be
|
|
// skipped. System-dependent event is set as the function's return value which
|
|
// can be nil when the event should not be passed on.
|
|
func decode(mask Event, e *event) (syse *event) {
|
|
if sysmask := uint32(mask) & e.sys.Mask; sysmask != 0 {
|
|
syse = &event{sys: syscall.InotifyEvent{
|
|
Wd: e.sys.Wd,
|
|
Mask: e.sys.Mask,
|
|
Cookie: e.sys.Cookie,
|
|
}, event: Event(sysmask), path: e.path}
|
|
}
|
|
imask := encode(mask)
|
|
switch {
|
|
case mask&Create != 0 && imask&uint32(InCreate|InMovedTo)&e.sys.Mask != 0:
|
|
e.event = Create
|
|
case mask&Remove != 0 && imask&uint32(InDelete|InDeleteSelf)&e.sys.Mask != 0:
|
|
e.event = Remove
|
|
case mask&Write != 0 && imask&uint32(InModify)&e.sys.Mask != 0:
|
|
e.event = Write
|
|
case mask&Rename != 0 && imask&uint32(InMovedFrom|InMoveSelf)&e.sys.Mask != 0:
|
|
e.event = Rename
|
|
default:
|
|
e.event = 0
|
|
}
|
|
return
|
|
}
|
|
|
|
// Unwatch implements notify.watcher interface. It looks for watch descriptor
|
|
// related to registered path and if found, calls inotify_rm_watch(2) function.
|
|
// This method is allowed to return EINVAL error when concurrently requested to
|
|
// delete identical path.
|
|
func (i *inotify) Unwatch(path string) (err error) {
|
|
iwd := int32(invalidDescriptor)
|
|
i.RLock()
|
|
for iwdkey, wd := range i.m {
|
|
if wd.path == path {
|
|
iwd = iwdkey
|
|
break
|
|
}
|
|
}
|
|
i.RUnlock()
|
|
if iwd == invalidDescriptor {
|
|
return errors.New("notify: path " + path + " is already watched")
|
|
}
|
|
fd := atomic.LoadInt32(&i.fd)
|
|
if _, err = syscall.InotifyRmWatch(int(fd), uint32(iwd)); err != nil {
|
|
return
|
|
}
|
|
i.Lock()
|
|
delete(i.m, iwd)
|
|
i.Unlock()
|
|
return nil
|
|
}
|
|
|
|
// Close implements notify.watcher interface. It removes all existing watch
|
|
// descriptors and wakes up producer goroutine by sending data to the write end
|
|
// of the pipe. The function waits for a signal from producer which means that
|
|
// all operations on current monitoring instance are done.
|
|
func (i *inotify) Close() (err error) {
|
|
i.Lock()
|
|
if fd := atomic.LoadInt32(&i.fd); fd == invalidDescriptor {
|
|
i.Unlock()
|
|
return nil
|
|
}
|
|
for iwd := range i.m {
|
|
if _, e := syscall.InotifyRmWatch(int(i.fd), uint32(iwd)); e != nil && err == nil {
|
|
err = e
|
|
}
|
|
delete(i.m, iwd)
|
|
}
|
|
switch _, errwrite := syscall.Write(i.pipefd[1], []byte{0x00}); {
|
|
case errwrite != nil && err == nil:
|
|
err = errwrite
|
|
fallthrough
|
|
case errwrite != nil:
|
|
i.Unlock()
|
|
default:
|
|
i.Unlock()
|
|
i.wg.Wait()
|
|
}
|
|
return
|
|
}
|