From b31d51e0b16f6233f2285adbab674ecfa2ace4ff Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sat, 5 Sep 2015 13:59:36 -0600 Subject: inotify: Avoid most of the race conditions, get rid of Cint There's still a condition that could be a race with fd-reuse, if one goroutine is calling inotify.{AddWatch,RmWatch,Read}(); another goroutine is calling inotify.Close(), and several things happen between loadFd() running and the add_watch/rm_watch/read syscall launching: - syscall.Close() returns - syscall.Open() reuses the filedescriptor A B syscall(loadFd()) inotify.Close(); syscall.Open() ---------------------------------------------------------- loadFd() syscall.Close() syscall.Open() syscall() Given that Read() can't be allowed to block Close() from running, I'm not sure there's a way to fix this. --- inotify/inotify.go | 50 ++++++++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 32 deletions(-) (limited to 'inotify/inotify.go') diff --git a/inotify/inotify.go b/inotify/inotify.go index 6d74830..f7db1b0 100644 --- a/inotify/inotify.go +++ b/inotify/inotify.go @@ -1,23 +1,21 @@ package inotify import ( - "errors" "syscall" "unsafe" + "sync" ) -var InotifyAlreadyClosedError error = errors.New("inotify instance already closed") - type Inotify struct { - fd Cint - isClosed bool + fd Fd fullbuff [4096]byte buff []byte + buffLock sync.Mutex } type Event struct { - Wd Cint /* Watch descriptor */ + Wd Wd /* Watch descriptor */ Mask Mask /* Mask describing event */ Cookie uint32 /* Unique cookie associating related events (for rename(2)) */ Name *string /* Optional name */ @@ -26,54 +24,42 @@ type Event struct { func InotifyInit() (*Inotify, error) { fd, err := inotify_init() o := Inotify{ - fd: Cint(fd), - isClosed: false, + fd: fd, } o.buff = o.fullbuff[:0] return &o, err } -func InotifyInit1(flags Cint) (*Inotify, error) { +func InotifyInit1(flags int) (*Inotify, error) { fd, err := inotify_init1(flags) o := Inotify{ - fd: Cint(fd), - isClosed: false, + fd: fd, } o.buff = o.fullbuff[:0] return &o, err } -func (o *Inotify) AddWatch(path string, mask Mask) (Cint, error) { - if o.isClosed { - return -1, InotifyAlreadyClosedError - } - return inotify_add_watch(o.fd, path, uint32(mask)) +func (o *Inotify) AddWatch(path string, mask Mask) (Wd, error) { + return inotify_add_watch(loadFd(&o.fd), path, mask) } -func (o *Inotify) RmWatch(wd Cint) error { - if o.isClosed { - return InotifyAlreadyClosedError - } - return inotify_rm_watch(o.fd, wd) +func (o *Inotify) RmWatch(wd Wd) error { + return inotify_rm_watch(loadFd(&o.fd), wd) } func (o *Inotify) Close() error { - if o.isClosed { - return InotifyAlreadyClosedError - } - o.isClosed = true - return sysclose(o.fd) + return sysclose(swapFd(&o.fd, -1)) } func (o *Inotify) Read() (Event, error) { + o.buffLock.Lock() + defer o.buffLock.Unlock() + if len(o.buff) == 0 { - if o.isClosed { - return Event{Wd: -1}, InotifyAlreadyClosedError - } - len, err := sysread(o.fd, o.buff) + len, err := sysread(loadFd(&o.fd), o.buff) if len == 0 { return Event{Wd: -1}, o.Close() - } else if len <= 0 { + } else if len < 0 { return Event{Wd: -1}, err } o.buff = o.fullbuff[0:len] @@ -81,7 +67,7 @@ func (o *Inotify) Read() (Event, error) { raw := (*syscall.InotifyEvent)(unsafe.Pointer(&o.buff[0])) ret := Event{ - Wd: Cint(raw.Wd), + Wd: Wd(raw.Wd), Mask: Mask(raw.Mask), Cookie: raw.Cookie, Name: nil, -- cgit v1.2.3