// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build linux #include #include #include #include #include "runtime.h" #include "defs.h" #include "malloc.h" #ifndef EPOLLRDHUP #define EPOLLRDHUP 0x2000 #endif #ifndef EPOLL_CLOEXEC #define EPOLL_CLOEXEC 02000000 #endif #ifndef HAVE_EPOLL_CREATE1 extern int epoll_create1(int __flags); #endif typedef struct epoll_event EpollEvent; static int32 runtime_epollcreate(int32 size) { int r; r = epoll_create(size); if(r >= 0) return r; return - errno; } static int32 runtime_epollcreate1(int32 flags) { int r; r = epoll_create1(flags); if(r >= 0) return r; return - errno; } static int32 runtime_epollctl(int32 epfd, int32 op, int32 fd, EpollEvent *ev) { int r; r = epoll_ctl(epfd, op, fd, ev); if(r >= 0) return r; return - errno; } static int32 runtime_epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout) { int r; r = epoll_wait(epfd, ev, nev, timeout); if(r >= 0) return r; return - errno; } static void runtime_closeonexec(int32 fd) { fcntl(fd, F_SETFD, FD_CLOEXEC); } static int32 epfd = -1; // epoll descriptor void runtime_netpollinit(void) { epfd = runtime_epollcreate1(EPOLL_CLOEXEC); if(epfd >= 0) return; epfd = runtime_epollcreate(1024); if(epfd >= 0) { runtime_closeonexec(epfd); return; } runtime_printf("netpollinit: failed to create descriptor (%d)\n", -epfd); runtime_throw("netpollinit: failed to create descriptor"); } int32 runtime_netpollopen(uintptr fd, PollDesc *pd) { EpollEvent ev; int32 res; ev.events = EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET; ev.data.ptr = (void*)pd; res = runtime_epollctl(epfd, EPOLL_CTL_ADD, (int32)fd, &ev); return -res; } int32 runtime_netpollclose(uintptr fd) { EpollEvent ev; int32 res; res = runtime_epollctl(epfd, EPOLL_CTL_DEL, (int32)fd, &ev); return -res; } // polls for ready network connections // returns list of goroutines that become runnable G* runtime_netpoll(bool block) { static int32 lasterr; EpollEvent events[128], *ev; int32 n, i, waitms, mode; G *gp; if(epfd == -1) return nil; waitms = -1; if(!block) waitms = 0; retry: n = runtime_epollwait(epfd, events, nelem(events), waitms); if(n < 0) { if(n != -EINTR && n != lasterr) { lasterr = n; runtime_printf("runtime: epollwait on fd %d failed with %d\n", epfd, -n); } goto retry; } gp = nil; for(i = 0; i < n; i++) { ev = &events[i]; if(ev->events == 0) continue; mode = 0; if(ev->events & (EPOLLIN|EPOLLRDHUP|EPOLLHUP|EPOLLERR)) mode += 'r'; if(ev->events & (EPOLLOUT|EPOLLHUP|EPOLLERR)) mode += 'w'; if(mode) runtime_netpollready(&gp, (void*)ev->data.ptr, mode); } if(block && gp == nil) goto retry; return gp; } void runtime_netpoll_scan(void (*addroot)(Obj)) { USED(addroot); }