poller

package module
v2.0.0+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 19, 2015 License: BSD-2-Clause Imports: 6 Imported by: 6

README

poller GoDoc

Package poller is a file-descriptor multiplexer.

Download:

go get github.com/npat-efault/poller

Package poller is a file-descriptor multiplexer. It allows concurent Read and Write operations from and to multiple file-descriptors without allocating one OS thread for every blocked operation. It operates similarly to Go's netpoller (which multiplexes network connections) without requiring special support from the Go runtime. It can be used with tty devices, character devices, pipes, FIFOs, and any file-descriptor that is poll-able (can be used with select(2), epoll(7), etc.) In addition, package poller allows the user to set timeouts (deadlines) for read and write operations, and also allows for safe cancelation of blocked read and write operations; a Close from another go-routine safely cancels ongoing (blocked) read and write operations.

Typical usage

fd, err: = poller.Open("/dev/ttyXX", poller.O_RW)
if err != nil {
    log.Fatal("Failed to open device:", err)
}

...

err = fd.SetReadDeadline(time.Now().Add(5 * time.Second))
if err != nil {
    log.Fatal("Failed to set R deadline:", err)
}

b := make([]byte, 10)
n, err := fd.Read(b)
if err != nil {
    log.Fatal("Read failed:", err)
}

...

n, err = fd.Write([]byte("Test"))
if err != nil {
    log.Fatal("Write failed:", err)
}

All operations on poller FDs are thread-safe; you can use the same FD from multiple go-routines. It is, for example, safe to close a file descriptor blocked on a Read or Write call from another go-routine.

Supported systems

Linux systems are supported using an implementations based on epoll(7).

For other POSIX systems (that is, most Unix-like systems), a more portable fallback implementation based on select(2) is provided. It has the same semantics as the Linux epoll(7)-based implementation, but is expected to be of lower performance. The select(2)-based implementation uses CGo for some ancillary select-related operations.

Ideally, system-specific io-multiplexing or async-io facilities (e.g. kqueue(2), or /dev/poll(7)) should be used to provide higher performance implementations for other systems. Patches for this will be greatly appreciated.

If you wish, you can build package poller on Linux to use the select(2)-based implementation instead of the epoll(7) one. To do this define the build-tag "noepoll". Normally, there is no reason to do this.

Documentation

Overview

Package poller is a file-descriptor multiplexer. It allows concurent Read and Write operations from and to multiple file-descriptors without allocating one OS thread for every blocked operation. It operates similarly to Go's netpoller (which multiplexes network connections) without requiring special support from the Go runtime. It can be used with tty devices, character devices, pipes, FIFOs, and any file-descriptor that is poll-able (can be used with select(2), epoll(7), etc.) In addition, package poller allows the user to set timeouts (deadlines) for read and write operations, and also allows for safe cancelation of blocked read and write operations; a Close from another go-routine safely cancels ongoing (blocked) read and write operations.

Typical usage

fd, err: = poller.Open("/dev/ttyXX", poller.O_RW)
if err != nil {
    log.Fatal("Failed to open device:", err)
}

...

err = fd.SetReadDeadline(time.Now().Add(5 * time.Second))
if err != nil {
    log.Fatal("Failed to set R deadline:", err)
}

b := make([]byte, 10)
n, err := fd.Read(b)
if err != nil {
    log.Fatal("Read failed:", err)
}

...

n, err = fd.Write([]byte("Test"))
if err != nil {
    log.Fatal("Write failed:", err)
}

All operations on poller FDs are thread-safe; you can use the same FD from multiple go-routines. It is, for example, safe to close a file descriptor blocked on a Read or Write call from another go-routine.

Supported systems

Linux systems are supported using an implementations based on epoll(7).

For other POSIX systems (that is, most Unix-like systems), a more portable fallback implementation based on select(2) is provided. It has the same semantics as the Linux epoll(7)-based implementation, but is expected to be of lower performance. The select(2)-based implementation uses CGo for some ancillary select-related operations.

Ideally, system-specific io-multiplexing or async-io facilities (e.g. kqueue(2), or /dev/poll(7)) should be used to provide higher performance implementations for other systems. Patches for this will be greatly appreciated.

If you wish, you can build package poller on Linux to use the select(2)-based implementation instead of the epoll(7) one. To do this define the build-tag "noepoll". Normally, there is no reason to do this.

Index

Constants

View Source
const (
	O_RW = (syscall.O_NOCTTY |
		syscall.O_CLOEXEC |
		syscall.O_RDWR |
		syscall.O_NONBLOCK) // Open file for read and write
	O_RO = (syscall.O_NOCTTY |
		syscall.O_CLOEXEC |
		syscall.O_RDONLY |
		syscall.O_NONBLOCK) // Open file for read
	O_WO = (syscall.O_NOCTTY |
		syscall.O_CLOEXEC |
		syscall.O_WRONLY |
		syscall.O_NONBLOCK) // Open file for write

)

Flags to Open

Variables

View Source
var (
	// Use of closed poller file-descriptor. ErrClosed has
	// Closed() == true.
	ErrClosed error = mkErr(efClosed, "use of closed descriptor")
	// Operation timed-out. ErrTimeout has Timeout() == true and
	// Temporary() == true.
	ErrTimeout error = mkErr(efTimeout, "timeout/deadline expired")
)

Errors returned by poller functions and methods. In addition to these, poller functions and methods may return the errors reported by the underlying system calls (open(2), read(2), write(2), etc.), as well as io.EOF and io.ErrUnexpectedEOF.

Functions

This section is empty.

Types

type FD

type FD struct {
	// contains filtered or unexported fields
}

FD is a poller file-descriptor. Typically a file-descriptor connected to a terminal, a pseudo terminal, a character device, a FIFO (named pipe), or any unix stream that can be used by the system's file-descriptor multiplexing mechanism (epoll(7), select(2), etc).

func NewFD

func NewFD(sysfd int) (*FD, error)

NewFD initializes and returns a new poller file-descriptor from the given system (unix) file-descriptor. After calling NewFD the system file-descriptor must be used only through the poller.FD methods, not directly. NewFD sets the system file-descriptor to non-blocking mode.

func Open

func Open(name string, flags int) (*FD, error)

Open the named path for reading, writing or both, depnding on the flags argument.

func (*FD) Close

func (fd *FD) Close() error

Close the file descriptor. It is ok to call Close concurently with other operations (Read, Write, SetDeadline, etc) on the same file-descriptor. In this case ongoing operations will return with ErrClosed. Any subsequent operations (after Close) on the file-descriptor will also fail with ErrClosed.

func (*FD) CloseUnlocked

func (fd *FD) CloseUnlocked() error

CloseUnlocked is equivalent to (*FD).Close, with the difference that with CloseUnlocked the caller is responsible for locking the FD before calling it, and unlocking it after. It can be useful for performing clean-up operations (e.g. reset tty settings) atomically with Close.

func (*FD) Lock

func (fd *FD) Lock() error

Lock the file-descriptor. It must be called before perfoming miscellaneous operations (e.g. ioctls) on the underlying system file descriptor. It protects the operations against concurent Close's. Typical usage is:

if err := fd.Lock(); err != nil {
    return err
}
deffer fd.Unlock()
... do ioctl on fd.Sysfd() ...

Notice that Read's and Write's *can* happen concurrently with misc operations on a locked FD---as this is not, necessarily, an error. Lock protects *only* against concurent Close's.

func (*FD) Read

func (fd *FD) Read(p []byte) (n int, err error)

Read up to len(p) bytes into p. Returns the number of bytes read (0 <= n <= len(p)) and any error encountered. If some data is available but not len(p) bytes, Read returns what is available instead of waiting for more. Read is compatible with the Read method of the io.Reader interface. In addition Read honors the timeout set by (*FD).SetDeadline and (*FD).SetReadDeadline. If no data are read before the timeout expires Read returns with err == ErrTimeout (and n == 0). If the read(2) system-call returns 0, Read returns with err = io.EOF (and n == 0).

func (*FD) SetDeadline

func (fd *FD) SetDeadline(t time.Time) error

SetDeadline sets the deadline for both Read and Write operations on the file-descriptor. Deadlines are expressed as ABSOLUTE instances in time. For example, to set a deadline 5 seconds to the future do:

fd.SetDeadline(time.Now().Add(5 * time.Second))

This is equivalent to:

fd.SetReadDeadline(time.Now().Add(5 * time.Second))
fd.SetWriteDeadline(time.Now().Add(5 * time.Second))

A zero value for t, cancels (removes) the existing deadline.

func (*FD) SetReadDeadline

func (fd *FD) SetReadDeadline(t time.Time) error

SetReadDeadline sets the deadline for Read operations on the file-descriptor.

func (*FD) SetWriteDeadline

func (fd *FD) SetWriteDeadline(t time.Time) error

SetWriteDeadline sets the deadline for Write operations on the file-descriptor.

func (*FD) Sysfd

func (fd *FD) Sysfd() int

Sysfd returns the system file-descriptor associated with the given poller file-descriptor. See also (*FD).Lock.

func (*FD) Unlock

func (fd *FD) Unlock()

Unlock unlocks the file-descriptor.

func (*FD) Write

func (fd *FD) Write(p []byte) (n int, err error)

Writes len(p) bytes from p to the file-descriptor. Returns the number of bytes written (0 <= n <= len(p)) and any error encountered that caused the write to stop early. Write returns a non-nil error if it returns n < len(p). Write is compatible with the Write method of the io.Writer interface. In addition Write honors the timeout set by (*FD).SetDeadline and (*FD).SetWriteDeadline. If less than len(p) data are writen before the timeout expires Write returns with err == ErrTimeout (and n < len(p)). If the write(2) system-call returns 0, Write returns with err == io.ErrUnexpectedEOF.

Write attempts to write the full amount requested by issuing multiple system calls if required. This sequence of system calls is NOT atomic. If multiple goroutines write to the same FD at the same time, their Writes may interleave. If you wish to protect against this, you can embed FD and use a mutex to guard Write method calls:

struct myFD {
    *poller.FD
    mu sync.Mutex
}

func (fd *myFD) Write(p []byte) (n int, err error) {
    fd.mu.Lock()
    n, err := fd.FD.Write(p)
    fd.mu.Unlock()
    return n, err
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL