]> go.fuhry.dev Git - fsnotify.git/commitdiff
Linux - use select to see if any watch data
authorChris Howey <chris@howey.me>
Fri, 7 Jun 2013 00:39:28 +0000 (19:39 -0500)
committerChris Howey <chris@howey.me>
Fri, 7 Jun 2013 00:39:28 +0000 (19:39 -0500)
With this fix, we can check if Close() has been called on the
Watcher after every select. Before the Read would block indefinately,
possibly hanging forever (user calling watch.Close() when no events
available).

fsnotify_linux.go

index d74b8e54bbc63bfc512c78e37d64be7beb15f0a1..53a5442cf4cb010077885a8085443a270782d45c 100644 (file)
@@ -54,6 +54,9 @@ const (
        sys_IN_IGNORED    uint32 = syscall.IN_IGNORED
        sys_IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW
        sys_IN_UNMOUNT    uint32 = syscall.IN_UNMOUNT
+
+       // Block for 100ms on each call to Select
+       selectWaitTime = 100e6
 )
 
 type FileEvent struct {
@@ -200,17 +203,37 @@ func (w *Watcher) readEvents() {
                errno error                                   // Syscall errno
        )
 
+       rfds := &syscall.FdSet{}
+       timeout := &syscall.Timeval{}
+
        for {
-               n, errno = syscall.Read(w.fd, buf[0:])
+               // Select to see if data available
+               *timeout = syscall.NsecToTimeval(selectWaitTime)
+               FD_ZERO(rfds)
+               FD_SET(rfds, w.fd)
+               if _, errno = syscall.Select(w.fd+1, rfds, nil, nil, timeout); errno != nil {
+                       w.Error <- os.NewSyscallError("select", errno)
+               }
+
                // See if there is a message on the "done" channel
-               var done bool
                select {
-               case done = <-w.done:
+               case <-w.done:
+                       syscall.Close(w.fd)
+                       close(w.internalEvent)
+                       close(w.Error)
+                       return
                default:
                }
 
-               // If EOF or a "done" message is received
-               if n == 0 || done {
+               // Check select result to see if Read will block, only read if no blocking.
+               if FD_ISSET(rfds, w.fd) {
+                       n, errno = syscall.Read(w.fd, buf[0:])
+               } else {
+                       continue
+               }
+
+               // If EOF is received
+               if n == 0 {
                        syscall.Close(w.fd)
                        close(w.internalEvent)
                        close(w.Error)
@@ -293,3 +316,17 @@ func (e *FileEvent) ignoreLinux() bool {
        }
        return false
 }
+
+func FD_SET(p *syscall.FdSet, i int) {
+       p.Bits[i/64] |= 1 << uint(i) % 64
+}
+
+func FD_ISSET(p *syscall.FdSet, i int) bool {
+       return (p.Bits[i/64] & (1 << uint(i) % 64)) != 0
+}
+
+func FD_ZERO(p *syscall.FdSet) {
+       for i := range p.Bits {
+               p.Bits[i] = 0
+       }
+}