init
This commit is contained in:
commit
2a86751b60
|
@ -0,0 +1,84 @@
|
||||||
|
# 简介
|
||||||
|
|
||||||
|
Linux Inotify API的封装。
|
||||||
|
|
||||||
|
# 使用示例
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"inotify"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
watcher, err := inotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
name := os.Args[1]
|
||||||
|
paths := make([]string, 0)
|
||||||
|
if !isExistDir(name) {
|
||||||
|
paths = append(paths, name)
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
paths, err = dirs(name)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, v := range paths {
|
||||||
|
err = watcher.AddWatch(v)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to add watch %s, error: %v\n", os.Args[1], err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker := time.NewTicker(3 * time.Second)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case err := <-watcher.Errors:
|
||||||
|
log.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
case event := <-watcher.Events:
|
||||||
|
if event.IsDir {
|
||||||
|
if event.Name == inotify.EventCreate {
|
||||||
|
err = watcher.AddWatch(event.Path)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to add watch %s, error: %v\n", os.Args[1], err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("Directory:", event.Path, "event:", event.Name)
|
||||||
|
} else {
|
||||||
|
fmt.Println("File:", event.Path, "event:", event.Name)
|
||||||
|
}
|
||||||
|
case <-ticker.C:
|
||||||
|
fmt.Println(watcher.Path())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isExistDir(name string) bool {
|
||||||
|
f, err := os.Stat(name)
|
||||||
|
return err == nil && f.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
func dirs(name string) ([]string, error) {
|
||||||
|
matchs := make([]string, 0)
|
||||||
|
err := filepath.Walk(name, func(root string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
matchs = append(matchs, root)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return matchs, err
|
||||||
|
}
|
||||||
|
```
|
|
@ -0,0 +1,76 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"inotify"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
watcher, err := inotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
name := os.Args[1]
|
||||||
|
paths := make([]string, 0)
|
||||||
|
if !isExistDir(name) {
|
||||||
|
paths = append(paths, name)
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
paths, err = dirs(name)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, v := range paths {
|
||||||
|
err = watcher.AddWatch(v)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to add watch %s, error: %v\n", os.Args[1], err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker := time.NewTicker(3 * time.Second)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case err := <-watcher.Errors:
|
||||||
|
log.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
case event := <-watcher.Events:
|
||||||
|
if event.IsDir {
|
||||||
|
if event.Name == inotify.EventCreate {
|
||||||
|
err = watcher.AddWatch(event.Path)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to add watch %s, error: %v\n", os.Args[1], err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("Directory:", event.Path, "event:", event.Name)
|
||||||
|
} else {
|
||||||
|
fmt.Println("File:", event.Path, "event:", event.Name)
|
||||||
|
}
|
||||||
|
case <-ticker.C:
|
||||||
|
fmt.Println(watcher.Path())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isExistDir(name string) bool {
|
||||||
|
f, err := os.Stat(name)
|
||||||
|
return err == nil && f.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
func dirs(name string) ([]string, error) {
|
||||||
|
matchs := make([]string, 0)
|
||||||
|
err := filepath.Walk(name, func(root string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
matchs = append(matchs, root)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return matchs, err
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
module inotify
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.0.0-20200519105757-fe76b779f299
|
|
@ -0,0 +1,2 @@
|
||||||
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
|
||||||
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
@ -0,0 +1,300 @@
|
||||||
|
package inotify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrEventOverflow = fmt.Errorf("inotify event queue overflowed")
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
Name string
|
||||||
|
Path string
|
||||||
|
IsDir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Watcher struct {
|
||||||
|
Events chan Event
|
||||||
|
Errors chan error
|
||||||
|
fd int
|
||||||
|
poller *poller
|
||||||
|
pW map[int32]watch
|
||||||
|
wP map[string]int
|
||||||
|
wPLock *sync.Mutex
|
||||||
|
pWLock *sync.Mutex
|
||||||
|
done chan bool
|
||||||
|
isClosed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) Path() []string {
|
||||||
|
paths := make([]string, 0)
|
||||||
|
|
||||||
|
if w.isClosed {
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|
||||||
|
w.wPLock.Lock()
|
||||||
|
defer w.wPLock.Unlock()
|
||||||
|
|
||||||
|
for path := range w.wP {
|
||||||
|
paths = append(paths, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWatcher() (*Watcher, error) {
|
||||||
|
fd, err := unix.InotifyInit1(unix.IN_NONBLOCK)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
poller, err := newPoller(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
w := &Watcher{
|
||||||
|
fd: fd,
|
||||||
|
poller: poller,
|
||||||
|
Events: make(chan Event),
|
||||||
|
Errors: make(chan error),
|
||||||
|
pW: make(map[int32]watch),
|
||||||
|
pWLock: new(sync.Mutex),
|
||||||
|
wP: make(map[string]int),
|
||||||
|
wPLock: new(sync.Mutex),
|
||||||
|
done: make(chan bool),
|
||||||
|
}
|
||||||
|
go w.eventLoop()
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const bufsize = unix.SizeofInotifyEvent + syscall.NAME_MAX + 1
|
||||||
|
|
||||||
|
type eventBuf [bufsize]byte
|
||||||
|
|
||||||
|
func (w *Watcher) readEvent(buf eventBuf, n int) {
|
||||||
|
var offset uint32
|
||||||
|
for offset <= uint32(n-unix.SizeofInotifyEvent) {
|
||||||
|
event := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
|
||||||
|
if event.Mask&unix.IN_Q_OVERFLOW == unix.IN_Q_OVERFLOW {
|
||||||
|
w.Errors <- ErrEventOverflow
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
var name string
|
||||||
|
w.pWLock.Lock()
|
||||||
|
path := w.pW[event.Wd]
|
||||||
|
w.pWLock.Unlock()
|
||||||
|
|
||||||
|
if event.Len > 0 {
|
||||||
|
rawName := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:event.Len:event.Len]
|
||||||
|
name = strings.TrimRight(string(rawName[0:event.Len]), "\000")
|
||||||
|
}
|
||||||
|
|
||||||
|
var isDir bool
|
||||||
|
if isDeleteSelf(event.Mask) {
|
||||||
|
isDir = path.isDir
|
||||||
|
w.remove(event.Wd)
|
||||||
|
} else {
|
||||||
|
isDir = isDirectory(event.Mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isIgnore(event.Mask) {
|
||||||
|
w.Events <- Event{
|
||||||
|
Name: maskString(event.Mask),
|
||||||
|
Path: filepath.Join(path.name, name),
|
||||||
|
IsDir: isDir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += event.Len + unix.SizeofInotifyEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) Close() {
|
||||||
|
if w.isClosed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.poller.close()
|
||||||
|
w.done <- true
|
||||||
|
close(w.Errors)
|
||||||
|
close(w.Events)
|
||||||
|
close(w.done)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) readEventLoop() {
|
||||||
|
var (
|
||||||
|
n int
|
||||||
|
err error
|
||||||
|
buf eventBuf
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-w.done:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = unix.Read(w.fd, buf[:])
|
||||||
|
if err != nil {
|
||||||
|
if err == unix.EINTR {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err == unix.EAGAIN {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
w.Errors <- err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < unix.SizeofInotifyEvent {
|
||||||
|
w.Errors <- fmt.Errorf("event is too short")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
w.readEvent(buf, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) eventLoop() {
|
||||||
|
var (
|
||||||
|
ok bool
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
ok, err = w.poller.wait()
|
||||||
|
if err != nil {
|
||||||
|
w.Errors <- err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
w.readEventLoop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) remove(wd int32) {
|
||||||
|
w.pWLock.Lock()
|
||||||
|
defer w.pWLock.Unlock()
|
||||||
|
name, ok := w.pW[wd]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(w.pW, wd)
|
||||||
|
|
||||||
|
w.wPLock.Lock()
|
||||||
|
defer w.wPLock.Unlock()
|
||||||
|
delete(w.wP, name.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
type watch struct {
|
||||||
|
name string
|
||||||
|
isDir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) AddWatch(name string) error {
|
||||||
|
name = filepath.Clean(name)
|
||||||
|
wd, err := unix.InotifyAddWatch(w.fd, name, unix.IN_ALL_EVENTS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.pWLock.Lock()
|
||||||
|
w.pW[int32(wd)] = watch{name: name, isDir: !isExistFile(name)}
|
||||||
|
w.pWLock.Unlock()
|
||||||
|
|
||||||
|
w.wPLock.Lock()
|
||||||
|
w.wP[name] = wd
|
||||||
|
w.wPLock.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) RemoveWatch(name string) error {
|
||||||
|
name = filepath.Clean(name)
|
||||||
|
w.wPLock.Lock()
|
||||||
|
defer w.wPLock.Unlock()
|
||||||
|
wd, ok := w.wP[name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
|
||||||
|
}
|
||||||
|
delete(w.wP, name)
|
||||||
|
|
||||||
|
w.pWLock.Lock()
|
||||||
|
defer w.pWLock.Unlock()
|
||||||
|
delete(w.pW, int32(wd))
|
||||||
|
|
||||||
|
_, err := unix.InotifyRmWatch(w.fd, uint32(wd))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func maskString(mask uint32) string {
|
||||||
|
for key, value := range eventName {
|
||||||
|
if key&mask != 0 {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EventUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
EventAccess = "IN_ACCESS"
|
||||||
|
EventAttrib = "IN_ATTRIB"
|
||||||
|
EventCloseWrite = "IN_CLOSE_WRITE"
|
||||||
|
EventCloseNoWrite = "IN_CLOSE_NOWRITE"
|
||||||
|
EventCreate = "IN_CREATE"
|
||||||
|
EventDelete = "IN_DELETE"
|
||||||
|
EventDeleteSelf = "IN_DELETE_SELF"
|
||||||
|
EventModify = "IN_MODIFY"
|
||||||
|
EventMoveSelf = "IN_MOVE_SELF"
|
||||||
|
EventMovedFrom = "IN_MOVED_FROM"
|
||||||
|
EventMovedTo = "IN_MOVED_TO"
|
||||||
|
EventOpen = "IN_OPEN"
|
||||||
|
EventIgnored = "IN_IGNORED"
|
||||||
|
EventUnmount = "IN_UNMOUNT"
|
||||||
|
EventUnknown = "UNKNOWN"
|
||||||
|
)
|
||||||
|
|
||||||
|
var eventName = map[uint32]string{
|
||||||
|
unix.IN_ACCESS: EventAccess,
|
||||||
|
unix.IN_ATTRIB: EventAttrib,
|
||||||
|
unix.IN_CLOSE_WRITE: EventCloseWrite,
|
||||||
|
unix.IN_CLOSE_NOWRITE: EventCloseNoWrite,
|
||||||
|
unix.IN_CREATE: EventCreate,
|
||||||
|
unix.IN_DELETE: EventDelete,
|
||||||
|
unix.IN_DELETE_SELF: EventDeleteSelf,
|
||||||
|
unix.IN_MODIFY: EventModify,
|
||||||
|
unix.IN_MOVE_SELF: EventMoveSelf,
|
||||||
|
unix.IN_MOVED_FROM: EventMovedFrom,
|
||||||
|
unix.IN_MOVED_TO: EventMovedTo,
|
||||||
|
unix.IN_OPEN: EventOpen,
|
||||||
|
unix.IN_IGNORED: EventIgnored,
|
||||||
|
unix.IN_UNMOUNT: EventUnmount,
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDirectory(mask uint32) bool {
|
||||||
|
return mask&unix.IN_ISDIR == unix.IN_ISDIR
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIgnore(mask uint32) bool {
|
||||||
|
return mask&unix.IN_IGNORED == unix.IN_IGNORED
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDeleteSelf(mask uint32) bool {
|
||||||
|
return mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF
|
||||||
|
}
|
||||||
|
|
||||||
|
func isExistFile(name string) bool {
|
||||||
|
f, err := os.Stat(name)
|
||||||
|
return err == nil && !f.IsDir()
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package inotify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
type poller struct {
|
||||||
|
fd int
|
||||||
|
epfd int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPoller(fd int) (*poller, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
p := &poller{fd: -1, epfd: -1}
|
||||||
|
p.fd = fd
|
||||||
|
p.epfd, err = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
event := unix.EpollEvent{
|
||||||
|
Fd: int32(p.fd),
|
||||||
|
Events: unix.EPOLLIN | unix.EPOLLET | unix.EPOLLERR | unix.EPOLLHUP,
|
||||||
|
}
|
||||||
|
err = unix.EpollCtl(p.epfd, unix.EPOLL_CTL_ADD, p.fd, &event)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *poller) wait() (bool, error) {
|
||||||
|
for {
|
||||||
|
events := make([]unix.EpollEvent, 1)
|
||||||
|
|
||||||
|
n, err := unix.EpollWait(p.epfd, events, -1)
|
||||||
|
if err != nil {
|
||||||
|
if err == unix.EINTR {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
event := events[0]
|
||||||
|
if event.Events&unix.EPOLLHUP != 0 {
|
||||||
|
return false, errors.New("epoll hup")
|
||||||
|
}
|
||||||
|
if event.Events&unix.EPOLLERR != 0 {
|
||||||
|
return false, errors.New("epoll error")
|
||||||
|
}
|
||||||
|
if event.Events&unix.EPOLLIN != 0 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, errors.New("unkown epoll event")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *poller) close() {
|
||||||
|
if p.fd != -1 {
|
||||||
|
unix.Close(p.fd)
|
||||||
|
}
|
||||||
|
if p.epfd != -1 {
|
||||||
|
unix.Close(p.epfd)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package inotify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPollerWithBadFd(t *testing.T) {
|
||||||
|
_, err := newPoller(-1)
|
||||||
|
if err != unix.EBADF {
|
||||||
|
t.Fatalf("Expected EBADF, got: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPollerWithData(t *testing.T) {
|
||||||
|
var tfd [2]int
|
||||||
|
err := unix.Pipe(tfd[:])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create pipe: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
unix.Close(tfd[0])
|
||||||
|
unix.Close(tfd[1])
|
||||||
|
}()
|
||||||
|
poller, err := newPoller(tfd[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create poller: %v", err)
|
||||||
|
}
|
||||||
|
oks := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
ok, err := poller.wait()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("poller failed: %v", err)
|
||||||
|
}
|
||||||
|
oks <- ok
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(1000 * time.Millisecond):
|
||||||
|
case <-oks:
|
||||||
|
t.Fatalf("poller did not wait")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := "poller"
|
||||||
|
buf := []byte(msg)
|
||||||
|
_, err = unix.Write(tfd[1], buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to write to pipe: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := <-oks
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected true")
|
||||||
|
}
|
||||||
|
buf2 := make([]byte, 2048)
|
||||||
|
n, err := unix.Read(tfd[0], buf2)
|
||||||
|
if string(buf2[:n]) != msg {
|
||||||
|
t.Fatalf("read data error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPollerWithClose(t *testing.T) {
|
||||||
|
var tfd [2]int
|
||||||
|
err := unix.Pipe(tfd[:])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create pipe: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
unix.Close(tfd[0])
|
||||||
|
}()
|
||||||
|
poller, err := newPoller(tfd[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create poller: %v", err)
|
||||||
|
}
|
||||||
|
unix.Close(tfd[1])
|
||||||
|
ok, err := poller.wait()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("except poller")
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
t.Fatalf("expected poller to return true")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue