On 7/26/16 4:05 PM, Matt Harden wrote: > Select and epool actually aren't useful for fd's that are opened on > regular files. As I understand it, they always indicate those files > are ready for I/O.
That may well be the case for regular files. I'm talking in particular about filehandles which are connected to a TTY, which is a character device. Pipes (another strong possibility for standard input/output) are also not regular files. In general it's up to the operating system what kind of file it is you get when you call open(2), or what any file descriptor you receive on process start-up is connected to. As a thought experiment I half mocked up a module for this called chanio; some highlights: // Reader declares extra methods over an io.Reader which allow data to // be read from channels and not just via the Read() method. type Reader interface { io.Reader ReadChan() <-chan []byte ReadError() <-chan error } // Writer declares extra methods over an io.Writer which allow data to // be read from channels and not just via the Writer() method. type Writer interface { io.Writer WriteChan() chan<- []byte WriteError() <-chan error } // WrappedReader wraps a regular io.Reader and satisfies the Reader // (and ReadCloser) interface type WrappedReader struct { rBuf []byte blockSz int readErr chan error R io.Reader rChan chan []byte } func (wr *WrappedReader) wrapRead(r io.Reader, chanSz, blockSz int) { wr.R = r wr.blockSz = blockSz wr.buf = nil wr.rChan = make(chan []byte, chanSz) wr.readErr = make(chan error) go wr.readLoop() } func (wr *WrappedReader) readLoop() { var n int var err error for { block := make([]byte, wr.blockSz) n, err = wr.R.Read(block) if err != nil { wr.readErr <- err // TODO: some errors are expected eg EINTR close(wr.rChan) return } if n != 0 { wr.rChan <- block[:n] } } } // Read allows a channel IO object to be used wherever a normal Reader is used func (wr *WrappedReader) Read(buf []byte) (int, error) { // buffered read if len(wr.rBuf) > len(buf) { buf[0:len(buf)] = wr.rBuf[0:len(buf)] wr.rBuf = wr.rBuf[len(buf):] return len(buf), nil } else if len(wr.rBuf) > 0 { // partial read n := len(wr.rBuf) buf[0:n] = wr.rBuf wr.rBuf = nil return n, nil } // no data ready - read from the channel select { case block := <-wr.ReadChan(): wr.rBuf = append(wr.rBuf, block) return wr.Read(buf) case err := <-wr.ReadError(): return -1, err } } func WrapReader(reader io.Reader) Reader { wr := &WrappedReader{} wr.wrapRead(reader, 1, 1024) return wr } func (wr *WrappedReader) ReadChan() <-chan []byte { return wr.rChan } func (wr *WrappedReader) ReadError() <-chan error { return wr.readErr } This might be a bit less awkward as a common API than having to use Read everywhere. Clearly it would be better if there was a way to create one of these chanio.Reader objects from something with a native select(2) backing for channel reads, which could be a different constructor. Thoughts? Sam -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.