iox

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Dec 13, 2025 License: MIT Imports: 2 Imported by: 0

README

iox

Non-blocking semantics for Go io package: first-class signals for would-block and multi-shot.

Go Reference Go Report Card Coverage Status

Language: English | 简体中文 | Español | 日本語 | Français

What this package is?

iox is for non-blocking I/O stacks where “no progress right now” and “progress now, but the operation remains active” are normal control flow, not failures.

It introduces two semantic errors with explicit contracts:

  • ErrWouldBlockno progress is possible now without waiting for readiness/completions. Return immediately; retry after your next polling.
  • ErrMoreprogress happened and the operation remains active; more events will follow. Process the current result and keep polling.

iox keeps standard io mental models intact:

  • returned counts always mean “bytes transferred / progress made”
  • returned errors drive control flow (nil, semantic non-failure, or real failure)
  • helpers are compatible with io.Reader, io.Writer, and optimize via io.WriterTo / io.ReaderFrom

Semantics contract

For operations that adopt iox semantics:

Return error Meaning What the caller must do next
nil completed successfully for this call / transfer continue your state machine
ErrWouldBlock no progress possible now stop attempting; wait for readiness/completion; retry
ErrMore progress happened; more completions will follow process now; keep the operation active; continue polling
other error failure handle/log/close/backoff as appropriate

Notes:

  • iox.Copy may return (written > 0, ErrWouldBlock) or (written > 0, ErrMore) to report partial progress before stalling or before delivering a multi-shot continuation.
  • (0, nil) reads are treated as “stop copying now” and return (written, nil) to avoid hidden spinning inside helpers.
Note: iox.Copy and (0, nil) reads

The Go io.Reader contract allows Read to return (0, nil) to mean “no progress”, not end-of-stream. Well-behaved Readers should avoid (0, nil) except when len(p) == 0.

iox.Copy intentionally treats a (0, nil) read as “stop copying now” and returns (written, nil). This avoids hidden spinning inside a helper in non-blocking/event-loop code. If you need strict forward-progress detection across repeated (0, nil), implement that policy at your call site.

Quick start

Install with go get:

go get code.hybscloud.com/iox
type reader struct{ step int }

func (r *reader) Read(p []byte) (int, error) {
	switch r.step {
	case 0:
		r.step++
		return copy(p, "hello"), iox.ErrMore
	case 1:
		r.step++
		return copy(p, "world"), nil
	case 2:
		r.step++
		return 0, iox.ErrWouldBlock
	case 3:
		r.step++
		return copy(p, "iox"), nil
	default:
		return 0, io.EOF
	}
}

func main() {
	src := &reader{}
	var dst bytes.Buffer

	n, err := iox.Copy(&dst, src)
	fmt.Printf("n=%d err=%v buf=%q\n", n, err, dst.String()) // n=5  err=io: expect more  buf="hello"
	_, _ = iox.CopyN(io.Discard, &dst, 5)                    // consume "hello"

	n, err = iox.Copy(&dst, src)
	fmt.Printf("n=%d err=%v buf=%q\n", n, err, dst.String()) // n=5  err=io: would block   buf="world"
	_, _ = iox.CopyN(io.Discard, &dst, 5)                    // consume "world"

	n, err = iox.Copy(&dst, src)
	fmt.Printf("n=%d err=%v buf=%q\n", n, err, dst.String()) // n=3  err=<nil>            buf="iox"
}

API overview

  • Errors

    • ErrWouldBlock, ErrMore
  • Copy

    • Copy(dst Writer, src Reader) (int64, error)
    • CopyBuffer(dst Writer, src Reader, buf []byte) (int64, error)
    • CopyN(dst Writer, src Reader, n int64) (int64, error)
    • CopyNBuffer(dst Writer, src Reader, n int64, buf []byte) (int64, error)
  • Tee

    • TeeReader(r Reader, w Writer) Reader
    • TeeWriter(primary, tee Writer) Writer
  • Adapters

    • AsWriterTo(r Reader) Reader (adds io.WriterTo via iox.Copy)
    • AsReaderFrom(w Writer) Writer (adds io.ReaderFrom via iox.Copy)
  • Semantics

    • IsNonFailure(err error) bool
    • IsWouldBlock(err error) bool
    • IsMore(err error) bool
    • IsProgress(err error) bool

Fast paths and semantic preservation

iox.Copy uses standard "io" fast paths when available:

  • if src implements io.WriterTo, iox.Copy calls WriteTo
  • else if dst implements io.ReaderFrom, iox.Copy calls ReadFrom
  • else it uses a fixed-size stack buffer (32KiB) and a read/write loop

To preserve ErrWouldBlock / ErrMore across fast paths, ensure your WriteTo / ReadFrom implementations return those errors when appropriate.

If you have a plain io.Reader/io.Writer but want the fast-path interfaces to exist and preserve semantics, wrap with:

  • iox.AsWriterTo(r) to add a WriteTo implemented via iox.Copy
  • iox.AsReaderFrom(w) to add a ReadFrom implemented via iox.Copy

License

MIT — see LICENSE.

©2025 Hayabusa Cloud Co., Ltd.

Documentation

Overview

Package iox provides non-blocking I/O helpers that extend Go's standard io semantics with explicit non-failure control-flow errors (see ErrWouldBlock, ErrMore) while remaining compatible with standard library interfaces.

IDE note: iox re-exports (aliases) the core io interfaces so that users can stay in the "iox" namespace while reading documentation and navigating types. The contracts below mirror the standard io expectations, with iox-specific behavior documented where relevant (typically at call sites such as Copy).

Index

Constants

This section is empty.

Variables

View Source
var (
	// EOF is returned by Read when no more input is available.
	// Functions should return EOF only to signal a graceful end of input.
	EOF = io.EOF

	// ErrClosedPipe is returned on write to a closed pipe.
	// It may also be returned by other operations that behave like a closed pipe.
	ErrClosedPipe = io.ErrClosedPipe

	// ErrNoProgress reports that a Reader returned no data and no error after
	// multiple Read calls. It is used by some io helpers to detect broken Readers
	// (i.e., lack of forward progress).
	ErrNoProgress = io.ErrNoProgress

	// ErrShortBuffer means a provided buffer was too small to complete the operation.
	// Callers typically retry with a larger buffer.
	ErrShortBuffer = io.ErrShortBuffer

	// ErrShortWrite means a write accepted fewer bytes than requested and returned
	// no explicit error (or equivalently, could not complete the full write).
	ErrShortWrite = io.ErrShortWrite

	// ErrUnexpectedEOF means EOF was encountered earlier than expected.
	// It is commonly used by fixed-size reads/copies when the stream ends mid-record.
	ErrUnexpectedEOF = io.ErrUnexpectedEOF
)

Common sentinel errors re-exported for convenience.

Note: iox also defines semantic non-failure errors (ErrWouldBlock, ErrMore) used by iox helpers and adapters; those are not part of the standard io set.

View Source
var ErrMore = errors.New("io: expect more")

ErrMore means “this operation remains active; more completions will follow” (multi-shot / streaming style). Next step: keep polling and processing results.

View Source
var ErrWouldBlock = errors.New("io: would block")

ErrWouldBlock means “no further progress without waiting”. Linux analogy: EAGAIN/EWOULDBLOCK / not-ready / no completion available. Next step: wait (via poll/epoll/io_uring/etc), then retry.

Functions

func Copy

func Copy(dst Writer, src Reader) (written int64, err error)

Copy copies from src to dst until either EOF is reached on src or an error occurs.

iox semantics extension:

  • ErrWouldBlock: return immediately because the next step would block. written may be > 0 (partial progress); retry after readiness/completion.
  • ErrMore: return immediately because progress happened and the operation remains active; written may be > 0; keep polling for more completions.

func CopyBuffer

func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)

CopyBuffer is like Copy but stages through buf if needed. If buf is nil, a stack buffer is used. If buf has zero length, CopyBuffer panics.

func CopyN

func CopyN(dst Writer, src Reader, n int64) (written int64, err error)

CopyN copies n bytes (or until an error) from src to dst. On return, written == n if and only if err == nil.

iox semantics extension:

  • ErrWouldBlock / ErrMore may be returned when progress stops early; written may be > 0 and is the number of bytes already copied.

func CopyNBuffer

func CopyNBuffer(dst Writer, src Reader, n int64, buf []byte) (written int64, err error)

CopyNBuffer is like CopyN but stages through buf if needed. If buf is nil, a stack buffer is used. If buf has zero length, CopyNBuffer panics.

func IsMore

func IsMore(err error) bool

IsMore reports whether err carries the iox multi-shot (more completions) semantic. It returns true for ErrMore and wrappers (via errors.Is).

func IsNonFailure

func IsNonFailure(err error) bool

IsNonFailure reports whether err should be treated as a non-failure in non-blocking I/O control flow: nil, ErrWouldBlock, or ErrMore.

Typical usage: decide whether to keep a descriptor active without logging an error or tearing down the operation.

func IsProgress

func IsProgress(err error) bool

IsProgress reports whether the current call produced usable progress now: returns true for nil and ErrMore. In both cases caller can proceed with delivered data/work; for ErrMore keep polling for subsequent completions.

func IsSemantic

func IsSemantic(err error) bool

IsSemantic reports whether err represents an iox semantic signal: either ErrWouldBlock or ErrMore (including wrapped forms).

func IsWouldBlock

func IsWouldBlock(err error) bool

IsWouldBlock reports whether err carries the iox would-block semantic. It returns true for ErrWouldBlock and wrappers (via errors.Is).

Types

type Buffer

type Buffer [32 * 1024]byte

Buffer is the default stack buffer used by Copy when none is supplied.

type ByteReader

type ByteReader = io.ByteReader

ByteReader reads and returns a single byte.

ByteReader is an alias of io.ByteReader.

type ByteScanner

type ByteScanner = io.ByteScanner

ByteScanner is a ByteReader that can "unread" the last byte read.

ByteScanner is an alias of io.ByteScanner.

type ByteWriter

type ByteWriter = io.ByteWriter

ByteWriter writes a single byte.

ByteWriter is an alias of io.ByteWriter.

type Closer

type Closer = io.Closer

Closer is implemented by types that can release resources.

Close should be idempotent where practical; callers should not assume any particular behavior beyond resource release and an error indicating failure.

Closer is an alias of io.Closer.

type Outcome

type Outcome uint8

Outcome classifies an operation result based on iox's extended semantics.

OutcomeOK: success, no more to come. OutcomeWouldBlock: no progress is possible right now; retry later. OutcomeMore: progress happened and more completions are expected. OutcomeFailure: any other error (including EOF when it's not absorbed by helpers).

const (
	OutcomeFailure Outcome = iota
	OutcomeOK
	OutcomeWouldBlock
	OutcomeMore
)

func Classify

func Classify(err error) Outcome

Classify maps err to an Outcome. Use when a compact switch is preferred.

Note: This does not attempt to reinterpret standard library sentinels like io.EOF; classification depends solely on the error value the caller passes.

func (Outcome) String

func (o Outcome) String() string

type ReadCloser

type ReadCloser = io.ReadCloser

ReadCloser groups Read and Close.

ReadCloser is an alias of io.ReadCloser.

type ReadSeeker

type ReadSeeker = io.ReadSeeker

ReadSeeker groups Read and Seek.

ReadSeeker is an alias of io.ReadSeeker.

type ReadWriteCloser

type ReadWriteCloser = io.ReadWriteCloser

ReadWriteCloser groups Read, Write, and Close.

ReadWriteCloser is an alias of io.ReadWriteCloser.

type ReadWriteSeeker

type ReadWriteSeeker = io.ReadWriteSeeker

ReadWriteSeeker groups Read, Write, and Seek.

ReadWriteSeeker is an alias of io.ReadWriteSeeker.

type ReadWriter

type ReadWriter = io.ReadWriter

ReadWriter groups the basic Read and Write methods.

ReadWriter is an alias of io.ReadWriter.

type Reader

type Reader = io.Reader

Reader is implemented by types that can read bytes into p.

Read must return the number of bytes read (0 <= n <= len(p)) and any error encountered. Even if Read returns n > 0, it may return a non-nil error to signal a condition observed after producing those bytes.

Callers should treat a return of (0, nil) as "no progress": it does not mean end-of-stream. Well-behaved implementations should avoid returning (0, nil) except when len(p) == 0.

Reader is an alias of io.Reader.

func AsWriterTo

func AsWriterTo(r Reader) Reader

AsWriterTo wraps r so that it also implements WriterTo via iox semantics.

func TeeReader

func TeeReader(r Reader, w Writer) Reader

TeeReader returns a Reader that writes to w what it reads from r. It mirrors io.TeeReader but propagates iox semantics:

  • If r.Read returns data with ErrWouldBlock or ErrMore, the data is first written to w, then the special error is returned unchanged.
  • If writing to w fails, that error is returned.
  • Short writes to w are reported as io.ErrShortWrite.

type ReaderAt

type ReaderAt = io.ReaderAt

ReaderAt reads from the underlying input at a given offset.

ReaderAt should not affect and should not be affected by the current seek offset. Implementations must return a non-nil error when n < len(p).

ReaderAt is an alias of io.ReaderAt.

type ReaderFrom

type ReaderFrom = io.ReaderFrom

ReaderFrom is an optional optimization for Writers.

If implemented by a Writer, Copy-like helpers may call ReadFrom to transfer data from r more efficiently than a generic read/write loop.

ReaderFrom is an alias of io.ReaderFrom.

type ReaderFromAdapter

type ReaderFromAdapter struct{ W Writer }

ReaderFromAdapter adapts a Writer to implement ReaderFrom using iox.Copy.

func (ReaderFromAdapter) ReadFrom

func (a ReaderFromAdapter) ReadFrom(src Reader) (int64, error)

ReadFrom delegates to iox.Copy to preserve extended semantics.

func (ReaderFromAdapter) Write

func (a ReaderFromAdapter) Write(p []byte) (int, error)

Write forwards to the underlying Writer to preserve Writer semantics.

type RuneReader

type RuneReader = io.RuneReader

RuneReader reads and returns a single UTF-8 encoded rune.

RuneReader is an alias of io.RuneReader.

type RuneScanner

type RuneScanner = io.RuneScanner

RuneScanner is a RuneReader that can "unread" the last rune read.

RuneScanner is an alias of io.RuneScanner.

type Seeker

type Seeker = io.Seeker

Seeker is implemented by types that can set the offset for the next Read or Write.

Seek sets the offset based on whence and returns the new absolute offset.

Seeker is an alias of io.Seeker.

type StringWriter

type StringWriter = io.StringWriter

StringWriter writes the contents of s more efficiently than Write([]byte(s)) for implementations that can avoid an allocation/copy.

StringWriter is an alias of io.StringWriter.

type WriteCloser

type WriteCloser = io.WriteCloser

WriteCloser groups Write and Close.

WriteCloser is an alias of io.WriteCloser.

type WriteSeeker

type WriteSeeker = io.WriteSeeker

WriteSeeker groups Write and Seek.

WriteSeeker is an alias of io.WriteSeeker.

type Writer

type Writer = io.Writer

Writer is implemented by types that can write bytes from p.

Write must return the number of bytes written (0 <= n <= len(p)) and any error encountered. If Write returns n < len(p), it must return a non-nil error (except in the special case of len(p) == 0).

Writer is an alias of io.Writer.

func AsReaderFrom

func AsReaderFrom(w Writer) Writer

AsReaderFrom wraps w so that it also implements ReaderFrom via iox semantics.

func TeeWriter

func TeeWriter(primary Writer, tee Writer) Writer

TeeWriter returns a Writer that duplicates all writes to primary and tee. If writing to primary returns an error or short count, it is returned immediately. Otherwise, the data is written to tee. If writing to tee fails or is short, the error (or io.ErrShortWrite) is returned. Special errors ErrWouldBlock and ErrMore are propagated unchanged.

type WriterAt

type WriterAt = io.WriterAt

WriterAt writes to the underlying output at a given offset.

WriterAt should not affect and should not be affected by the current seek offset. Implementations must return a non-nil error when n < len(p).

WriterAt is an alias of io.WriterAt.

type WriterTo

type WriterTo = io.WriterTo

WriterTo is an optional optimization for Readers.

If implemented by a Reader, Copy-like helpers may call WriteTo to transfer data to w more efficiently than a generic read/write loop.

WriterTo is an alias of io.WriterTo.

type WriterToAdapter

type WriterToAdapter struct{ R Reader }

WriterToAdapter adapts a Reader to implement WriterTo using iox.Copy.

func (WriterToAdapter) Read

func (a WriterToAdapter) Read(p []byte) (int, error)

Read forwards to the underlying Reader to preserve Reader semantics.

func (WriterToAdapter) WriteTo

func (a WriterToAdapter) WriteTo(dst Writer) (int64, error)

WriteTo delegates to iox.Copy to preserve extended semantics.

Directories

Path Synopsis
Package examples contains small, runnable snippets that demonstrate how to use iox's non-blocking semantics in typical copy-style flows.
Package examples contains small, runnable snippets that demonstrate how to use iox's non-blocking semantics in typical copy-style flows.

Jump to

Keyboard shortcuts

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