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.

Reply via email to