├── README.md ├── debug.go ├── epoll_linux.go ├── epoll_linux_386.go ├── epoll_linux_amd64.go ├── epoll_linux_arm.go ├── epoll_linux_test.go ├── poller.go ├── poller_test.go ├── release.go └── select.go /README.md: -------------------------------------------------------------------------------- 1 | 2 | # poller 3 | import "github.com/pkg/poller" 4 | 5 | Package poller provides level triggered readiness notification and 6 | reliable closing of file descriptors. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ## type Pollable 15 | ``` go 16 | type Pollable struct { 17 | // contains filtered or unexported fields 18 | } 19 | ``` 20 | Pollable represents a file descriptor that can be read/written 21 | and polled/waited for readiness notification. 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ### func (\*Pollable) Close 34 | ``` go 35 | func (p *Pollable) Close() error 36 | ``` 37 | Close deregisters the Pollable and closes the underlying file descriptor. 38 | 39 | 40 | 41 | ### func (\*Pollable) Read 42 | ``` go 43 | func (p *Pollable) Read(b []byte) (int, error) 44 | ``` 45 | Read reads up to len(b) bytes from the underlying fd. It returns the number of 46 | bytes read and an error, if any. EOF is signaled by a zero count with 47 | err set to io.EOF. 48 | 49 | Callers to Read will block if there is no data available to read. 50 | 51 | 52 | 53 | ### func (\*Pollable) WaitRead 54 | ``` go 55 | func (p *Pollable) WaitRead() error 56 | ``` 57 | WaitRead waits for the Pollable to become ready for 58 | reading. 59 | 60 | 61 | 62 | ### func (\*Pollable) WaitWrite 63 | ``` go 64 | func (p *Pollable) WaitWrite() error 65 | ``` 66 | WaitWrite waits for the Pollable to become ready for 67 | writing. 68 | 69 | 70 | 71 | ### func (\*Pollable) Write 72 | ``` go 73 | func (p *Pollable) Write(b []byte) (int, error) 74 | ``` 75 | Write writes len(b) bytes to the fd. It returns the number of bytes 76 | written and an error, if any. Write returns a non-nil error when n != 77 | len(b). 78 | 79 | Callers to Write will block if there is no buffer capacity available. 80 | 81 | 82 | 83 | ## type Poller 84 | ``` go 85 | type Poller struct { 86 | // contains filtered or unexported fields 87 | } 88 | ``` 89 | A Poller provides readiness notification and reliable closing of 90 | registered file descriptors. 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | ### func New 101 | ``` go 102 | func New() (*Poller, error) 103 | ``` 104 | New creates a new Poller. 105 | 106 | 107 | 108 | 109 | ### func (\*Poller) Register 110 | ``` go 111 | func (p *Poller) Register(fd uintptr) (*Pollable, error) 112 | ``` 113 | Register registers a file describtor with the Poller and returns a 114 | Pollable which can be used for reading/writing as well as readiness 115 | notification. 116 | 117 | File descriptors registered with the poller will be placed into 118 | non-blocking mode. 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | - - - 129 | Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md) -------------------------------------------------------------------------------- /debug.go: -------------------------------------------------------------------------------- 1 | // +build debug 2 | 3 | package poller 4 | 5 | import "log" 6 | 7 | var debug = log.Printf 8 | -------------------------------------------------------------------------------- /epoll_linux.go: -------------------------------------------------------------------------------- 1 | package poller 2 | 3 | // epoll(2) poller 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | "unsafe" 9 | ) 10 | 11 | // New creates a new Poller. 12 | func New() (*Poller, error) { 13 | p, err := newEpoll() 14 | return &Poller{poller: p}, err 15 | } 16 | 17 | // newEpoll returns an epoll(2) poller implementation. 18 | func newEpoll() (poller, error) { 19 | fd, err := epollCreate1() 20 | if err != nil { 21 | return nil, err 22 | } 23 | e := epoll{ 24 | pollfd: fd, 25 | } 26 | go e.loop() 27 | return &e, nil 28 | } 29 | 30 | // epoll implements an epoll(2) based poller. 31 | type epoll struct { 32 | pollfd uintptr 33 | eventbuf [0x10]event 34 | events []event 35 | } 36 | 37 | func (e *epoll) register(fd uintptr) (*Pollable, error) { 38 | p := Pollable{ 39 | fd: fd, 40 | cr: make(chan error), 41 | cw: make(chan error), 42 | poller: e, 43 | } 44 | ev := event{ 45 | events: syscall.EPOLLERR | syscall.EPOLLHUP, 46 | } 47 | ev.setdata(&p) 48 | if err := epollctl(e.pollfd, syscall.EPOLL_CTL_ADD, fd, &ev); err != nil { 49 | return nil, err 50 | } 51 | debug("epoll: register: fd: %v, %p", p.fd, &p) 52 | return &p, nil 53 | } 54 | 55 | func (e *epoll) deregister(p *Pollable) error { 56 | // TODO(dfc) // wakeup all other waiters ? 57 | return epollctl(e.pollfd, syscall.EPOLL_CTL_DEL, p.fd, nil) 58 | } 59 | 60 | func (e *epoll) loop() { 61 | defer syscall.Close(int(e.pollfd)) 62 | for { 63 | ev, err := e.wait() 64 | if err != nil { 65 | println(err.Error()) 66 | return 67 | } 68 | if ev == nil { 69 | // timeout / wakeup ? 70 | continue 71 | } 72 | mode := int('r') 73 | if ev.events&syscall.EPOLLOUT == syscall.EPOLLOUT { 74 | mode = 'w' 75 | } 76 | ev.getdata().wake(mode, nil) 77 | } 78 | } 79 | 80 | func (e *epoll) wait() (*event, error) { 81 | for len(e.events) == 0 { 82 | const msec = -1 83 | n, err := epollwait(e.pollfd, e.eventbuf[0:], msec) 84 | debug("epoll: epollwait: %v, %v", n, err) 85 | if err != nil { 86 | if err == syscall.EAGAIN || err == syscall.EINTR { 87 | continue 88 | } 89 | return nil, os.NewSyscallError("epoll_wait", err) 90 | } 91 | if n == 0 { 92 | return nil, nil 93 | } 94 | e.events = e.eventbuf[0:n] 95 | } 96 | ev := e.events[0] 97 | e.events = e.events[1:] 98 | debug("epoll: wait: %0x, %p, fd: %v", ev.events, ev.getdata(), ev.getdata().fd) 99 | return &ev, nil 100 | } 101 | 102 | func (e *epoll) waitRead(p *Pollable) error { 103 | ev := event{ 104 | events: syscall.EPOLLONESHOT | syscall.EPOLLIN, 105 | } 106 | ev.setdata(p) 107 | debug("epoll: waitRead: %d, %0x, %p", p.fd, ev.events, ev.getdata()) 108 | return epollctl(e.pollfd, syscall.EPOLL_CTL_MOD, p.fd, &ev) 109 | } 110 | func (e *epoll) waitWrite(p *Pollable) error { 111 | ev := event{ 112 | events: syscall.EPOLLONESHOT | syscall.EPOLLOUT, 113 | } 114 | ev.setdata(p) 115 | debug("epoll: waitWrite: %0x, %p", ev.events, ev.getdata()) 116 | return epollctl(e.pollfd, syscall.EPOLL_CTL_MOD, p.fd, &ev) 117 | } 118 | 119 | func epollCreate1() (uintptr, error) { 120 | fd, _, e := syscall.Syscall(syscall.SYS_EPOLL_CREATE1, syscall.EPOLL_CLOEXEC, 0, 0) 121 | if e != 0 { 122 | return 0, e 123 | } 124 | return fd, nil 125 | } 126 | 127 | // Single-word zero for use when we need a valid pointer to 0 bytes. 128 | var _zero uintptr 129 | 130 | func epollwait(epfd uintptr, events []event, msec int) (n int, err error) { 131 | var _p0 unsafe.Pointer 132 | if len(events) > 0 { 133 | _p0 = unsafe.Pointer(&events[0]) 134 | } else { 135 | _p0 = unsafe.Pointer(&_zero) 136 | } 137 | r0, _, e1 := syscall.Syscall6(syscall.SYS_EPOLL_WAIT, epfd, uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0) 138 | n = int(r0) 139 | if e1 != 0 { 140 | err = e1 141 | } 142 | return 143 | } 144 | 145 | func epollctl(epfd uintptr, op int, fd uintptr, event *event) (err error) { 146 | _, _, e1 := syscall.RawSyscall6(syscall.SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), fd, uintptr(unsafe.Pointer(event)), 0, 0) 147 | if e1 != 0 { 148 | err = e1 149 | } 150 | return 151 | } 152 | -------------------------------------------------------------------------------- /epoll_linux_386.go: -------------------------------------------------------------------------------- 1 | package poller 2 | 3 | type event struct { 4 | events uint32 5 | _ uint32 6 | data *Pollable 7 | } 8 | 9 | func (e *event) setdata(p *Pollable) { 10 | e.data = p 11 | } 12 | 13 | func (e *event) getdata() *Pollable { 14 | return e.data 15 | } 16 | -------------------------------------------------------------------------------- /epoll_linux_amd64.go: -------------------------------------------------------------------------------- 1 | package poller 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | // The epoll event stucture is defined, on all platforms, to be a uint32 followed directly by 8 | // a uint64. The former is used by epoll for flags and status, the latter is defined to be the 9 | // target of a C union, but is effectively 64 bits of data that can be used as a key for 10 | // the caller. 11 | // 12 | // But, there is a problem. Ideally we'd like to write this as 13 | // 14 | // type event struct { 15 | // events uint32 16 | // data *Pollable 17 | // } 18 | // 19 | // and then epollwait will return us *event's that contain the pointer to the *Pollable that 20 | // the event describes -- however it's not that simple. 21 | // 22 | // 1. On 64bit platforms, a pointer is 64bits, so will be 64bit alliged, as the field before 23 | // it is 32bits wide, there will be padding added by 6g and that means that the size of 24 | // event is 16 bytes, not 12, so a []event will be incorrectly aligned. 25 | // 26 | // Also, because of the invisible padding, the pointer stored in data is truncated, ie only 27 | // the bottom 4 bytes are preserved, so calling a method on the *Pollable returned will 28 | // segfault. 29 | // 30 | // 2. On 32bit platforms the opposite is true. The size of data is 32bits, and thus requires no 31 | // padding for alignment. So, anything pointer stored in data will be returned faithfully, but 32 | // a []event will be incorrectly aligned because the size of event on 32bit platforms is only 33 | // 8 bytes, not the expected 12. 34 | // 35 | // To overcome the problem, this declaration handles data as 8 bytes of memory then we use 36 | // unsafe to convert the value to/from a *Pollable as required. For 32 bit platforms, the 37 | // declaration is simple and includes the required padding. 38 | // 39 | // On both platforms getdata and setdata inline so the cost of this slight of hand is minimal. 40 | type event struct { 41 | events uint32 42 | data [2]uint32 43 | } 44 | 45 | func (e *event) setdata(p *Pollable) { 46 | *(**Pollable)(unsafe.Pointer(&e.data[0])) = p 47 | } 48 | 49 | func (e *event) getdata() *Pollable { 50 | return *(**Pollable)(unsafe.Pointer(&e.data[0])) 51 | } 52 | -------------------------------------------------------------------------------- /epoll_linux_arm.go: -------------------------------------------------------------------------------- 1 | package poller 2 | 3 | type event struct { 4 | events uint32 5 | _ uint32 6 | data *Pollable 7 | } 8 | 9 | func (e *event) setdata(p *Pollable) { 10 | e.data = p 11 | } 12 | 13 | func (e *event) getdata() *Pollable { 14 | return e.data 15 | } 16 | -------------------------------------------------------------------------------- /epoll_linux_test.go: -------------------------------------------------------------------------------- 1 | package poller 2 | 3 | var _ poller = new(epoll) 4 | -------------------------------------------------------------------------------- /poller.go: -------------------------------------------------------------------------------- 1 | // Package poller provides level triggered readiness notification and 2 | // reliable closing of file descriptors. 3 | package poller 4 | 5 | import ( 6 | "io" 7 | "syscall" 8 | ) 9 | 10 | // A Poller provides readiness notification and reliable closing of 11 | // registered file descriptors. 12 | type Poller struct { 13 | poller 14 | } 15 | 16 | // Register registers a file describtor with the Poller and returns a 17 | // Pollable which can be used for reading/writing as well as readiness 18 | // notification. 19 | // 20 | // File descriptors registered with the poller will be placed into 21 | // non-blocking mode. 22 | func (p *Poller) Register(fd uintptr) (*Pollable, error) { 23 | if err := syscall.SetNonblock(int(fd), true); err != nil { 24 | return nil, err 25 | } 26 | return p.register(fd) 27 | } 28 | 29 | // Pollable represents a file descriptor that can be read/written 30 | // and polled/waited for readiness notification. 31 | type Pollable struct { 32 | fd uintptr 33 | cr, cw chan error 34 | poller 35 | } 36 | 37 | // Read reads up to len(b) bytes from the underlying fd. It returns the number of 38 | // bytes read and an error, if any. EOF is signaled by a zero count with 39 | // err set to io.EOF. 40 | // 41 | // Callers to Read will block if there is no data available to read. 42 | func (p *Pollable) Read(b []byte) (int, error) { 43 | n, e := p.read(b) 44 | if n < 0 { 45 | n = 0 46 | } 47 | if n == 0 && len(b) > 0 && e == nil { 48 | return 0, io.EOF 49 | } 50 | if e != nil { 51 | return n, e 52 | } 53 | return n, nil 54 | } 55 | 56 | func (p *Pollable) read(b []byte) (int, error) { 57 | for { 58 | n, e := syscall.Read(int(p.fd), b) 59 | if e != syscall.EAGAIN { 60 | return n, e 61 | } 62 | if err := p.WaitRead(); err != nil { 63 | return 0, err 64 | } 65 | } 66 | } 67 | 68 | // Write writes len(b) bytes to the fd. It returns the number of bytes 69 | // written and an error, if any. Write returns a non-nil error when n != 70 | // len(b). 71 | // 72 | // Callers to Write will block if there is no buffer capacity available. 73 | func (p *Pollable) Write(b []byte) (int, error) { 74 | n, e := p.write(b) 75 | if n < 0 { 76 | n = 0 77 | } 78 | if n != len(b) { 79 | return n, io.ErrShortWrite 80 | } 81 | if e != nil { 82 | return n, e 83 | } 84 | return n, nil 85 | } 86 | 87 | func (p *Pollable) write(b []byte) (int, error) { 88 | for { 89 | // TODO(dfc) this is wrong 90 | n, e := syscall.Write(int(p.fd), b) 91 | if e != syscall.EAGAIN { 92 | return n, e 93 | } 94 | if err := p.WaitWrite(); err != nil { 95 | return 0, err 96 | } 97 | } 98 | } 99 | 100 | // Close deregisters the Pollable and closes the underlying file descriptor. 101 | func (p *Pollable) Close() error { 102 | err := p.deregister(p) 103 | // p.fd = uintptr(-1) // TODO(dfc) fix 104 | return err 105 | } 106 | 107 | // WaitRead waits for the Pollable to become ready for 108 | // reading. 109 | func (p *Pollable) WaitRead() error { 110 | debug("pollable: %p, fd: %v waitread", p, p.fd) 111 | if err := p.poller.waitRead(p); err != nil { 112 | return err 113 | } 114 | return <-p.cr 115 | } 116 | 117 | // WaitWrite waits for the Pollable to become ready for 118 | // writing. 119 | func (p *Pollable) WaitWrite() error { 120 | if err := p.poller.waitWrite(p); err != nil { 121 | return err 122 | } 123 | return <-p.cw 124 | } 125 | 126 | func (p *Pollable) wake(mode int, err error) { 127 | debug("pollable: %p, fd: %v wake: %c, %v", p, p.fd, mode, err) 128 | if mode == 'r' { 129 | p.cr <- err 130 | } else { 131 | p.cw <- err 132 | } 133 | } 134 | 135 | type poller interface { 136 | register(fd uintptr) (*Pollable, error) 137 | waitRead(*Pollable) error 138 | waitWrite(*Pollable) error 139 | deregister(*Pollable) error 140 | } 141 | -------------------------------------------------------------------------------- /poller_test.go: -------------------------------------------------------------------------------- 1 | package poller 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestWaitRead(t *testing.T) { 10 | p, err := New() 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | //defer p.Close() 15 | 16 | pr, pw, err := os.Pipe() 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | 21 | r, err := p.Register(pr.Fd()) 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | defer r.Close() 26 | 27 | w, err := p.Register(pw.Fd()) 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | defer w.Close() 32 | 33 | ready := make(chan bool) 34 | go func() { 35 | <-ready 36 | <-time.After(50 * time.Millisecond) 37 | if _, err := w.Write([]byte("hello")); err != nil { 38 | t.Error(err) 39 | } 40 | }() 41 | 42 | read := make(chan []byte) 43 | go func() { 44 | var buf [5]byte 45 | if n, err := r.Read(buf[:]); err != nil || n != 5 { 46 | t.Error(n, err) 47 | } 48 | read <- buf[:] 49 | }() 50 | close(ready) 51 | select { 52 | case buf := <-read: 53 | if string(buf) != "hello" { 54 | t.Fatalf("expected %q, got %q", "hello", buf) 55 | } 56 | case <-time.After(200 * time.Millisecond): 57 | t.Fatal("timeout") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /release.go: -------------------------------------------------------------------------------- 1 | // +build !debug 2 | 3 | package poller 4 | 5 | func debug(string, ...interface{}) {} 6 | -------------------------------------------------------------------------------- /select.go: -------------------------------------------------------------------------------- 1 | // +build none 2 | 3 | package poller 4 | 5 | // select(2) poller 6 | 7 | import ( 8 | "os" 9 | ) 10 | 11 | // newSelect returns a select(2) poller implementation. 12 | func newSelect() (poller, error) { 13 | r, w, err := os.Pipe() 14 | if err != nil { 15 | return nil, err 16 | } 17 | _ = r 18 | return &_select{ 19 | wakefd: w, 20 | }, nil 21 | } 22 | 23 | // _select implements a select(2) based poller. 24 | // The underscore is due to an unfortunate conflict with 25 | // the select keyword. 26 | type _select struct { 27 | wakefd *os.File 28 | } 29 | --------------------------------------------------------------------------------