]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: explicitly remove fd's from epoll waitset before close()
authorDmitriy Vyukov <dvyukov@google.com>
Thu, 21 Mar 2013 08:54:19 +0000 (12:54 +0400)
committerDmitriy Vyukov <dvyukov@google.com>
Thu, 21 Mar 2013 08:54:19 +0000 (12:54 +0400)
Fixes #5061.

Current code relies on the fact that fd's are automatically removed from epoll set when closed. However, it is not true. Underlying file description is removed from epoll set only when *all* fd's referring to it are closed.

There are 2 bad consequences:
1. Kernel delivers notifications on already closed fd's.
2. The following sequence of events leads to error:
   - add fd1 to epoll
   - dup fd1 = fd2
   - close fd1 (not removed from epoll since we've dup'ed the fd)
   - dup fd2 = fd1 (get the same fd as fd1)
   - add fd1 to epoll = EEXIST

So, if fd can be potentially dup'ed of fork'ed, it's necessary to explicitly remove the fd from epoll set.

R=golang-dev, bradfitz, dave
CC=golang-dev
https://golang.org/cl/7870043

src/pkg/net/fd_unix.go
src/pkg/runtime/netpoll.goc
src/pkg/runtime/netpoll_epoll.c
src/pkg/runtime/netpoll_kqueue.c
src/pkg/runtime/runtime.h

index cc5a030acbc24bf21fe1dbef850fd63a18e5b078..2b418a86812149cedcb90a247e3932c96fc129ce 100644 (file)
@@ -124,8 +124,10 @@ func (fd *netFD) decref() {
        fd.sysmu.Lock()
        fd.sysref--
        if fd.closing && fd.sysref == 0 && fd.sysfile != nil {
-               fd.sysfile.Close()
+               // Poller may want to unregister fd in readiness notification mechanism,
+               // so this must be executed before sysfile.Close().
                fd.pd.Close()
+               fd.sysfile.Close()
                fd.sysfile = nil
                fd.sysfd = -1
        }
index b314c653385b70c210d18878c369ac89247d817b..06b6d61727fc481c4a0fc933e6043a76deb9d20b 100644 (file)
@@ -25,6 +25,7 @@ struct PollDesc
 {
        PollDesc* link; // in pollcache, protected by pollcache.Lock
        Lock;           // protectes the following fields
+       int32   fd;
        bool    closing;
        uintptr seq;    // protects from stale timers and ready notifications
        G*      rg;     // G waiting for read or READY (binary semaphore)
@@ -69,6 +70,7 @@ func runtime_pollOpen(fd int) (pd *PollDesc, errno int) {
                runtime·throw("runtime_pollOpen: blocked write on free descriptor");
        if(pd->rg != nil && pd->rg != READY)
                runtime·throw("runtime_pollOpen: blocked read on free descriptor");
+       pd->fd = fd;
        pd->closing = false;
        pd->seq++;
        pd->rg = nil;
@@ -87,6 +89,7 @@ func runtime_pollClose(pd *PollDesc) {
                runtime·throw("runtime_pollClose: blocked write on closing descriptor");
        if(pd->rg != nil && pd->rg != READY)
                runtime·throw("runtime_pollClose: blocked read on closing descriptor");
+       runtime·netpollclose(pd->fd);
        runtime·lock(&pollcache);
        pd->link = pollcache.first;
        pollcache.first = pd;
index 34ed78addb1817f5404e2e0c98f201bcf52c2509..d6ef0d1446c78b090ccf02d6ff818eb7f20a7153 100644 (file)
@@ -34,10 +34,22 @@ int32
 runtime·netpollopen(int32 fd, PollDesc *pd)
 {
        EpollEvent ev;
+       int32 res;
 
        ev.events = EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET;
        ev.data = (uint64)pd;
-       return runtime·epollctl(epfd, EPOLL_CTL_ADD, fd, &ev);
+       res = runtime·epollctl(epfd, EPOLL_CTL_ADD, fd, &ev);
+       return -res;
+}
+
+int32
+runtime·netpollclose(int32 fd)
+{
+       EpollEvent ev;
+       int32 res;
+
+       res = runtime·epollctl(epfd, EPOLL_CTL_DEL, fd, &ev);
+       return -res;
 }
 
 // polls for ready network connections
index 7603260565044e718dfd3e5883205bbaffbe5b12..ad721e293eea42cf107aad937d0a4493e960bd52 100644 (file)
@@ -57,6 +57,15 @@ runtime·netpollopen(int32 fd, PollDesc *pd)
        return 0;
 }
 
+int32
+runtime·netpollclose(int32 fd)
+{
+       // Don't need to unregister because calling close()
+       // on fd will remove any kevents that reference the descriptor.
+       USED(fd);
+       return 0;
+}
+
 // Polls for ready network connections.
 // Returns list of goroutines that become runnable.
 G*
index d209a4dfcaf5401915b373944ee1b0638f46cdd2..46c77e3fd5e221882e189be183fef91c4b07382c 100644 (file)
@@ -792,6 +792,7 @@ bool        runtime·deltimer(Timer*);
 G*     runtime·netpoll(bool);
 void   runtime·netpollinit(void);
 int32  runtime·netpollopen(int32, PollDesc*);
+int32   runtime·netpollclose(int32);
 void   runtime·netpollready(G**, PollDesc*, int32);
 void   runtime·crash(void);