Skip to content

Releases: hayabusa-cloud/iox

iox v0.3.6 — Clarify semantic control classification

04 May 08:12
Immutable release. Only release title and notes can be modified.
b0a318d

Choose a tag to compare

Introduction

Package iox provides non-blocking I/O semantics for Go, extending the standard io package with explicit control outcomes for readiness suspension and live streaming or multishot frontiers while preserving ordinary io.Reader and io.Writer compatibility.

Blocking I/O code often treats (count, error) as a simple success or failure boundary. Non-blocking and streaming I/O also need to expose "not ready yet" and "this operation is still alive." iox makes both cases explicit: ErrWouldBlock means the immediate attempt reached a no-progress readiness boundary and should be retried only after readiness or event evidence, while ErrMore means the current observation is usable and the same logical operation can yield later observations before it terminates, is canceled, or fails.

This release tightens the public classification contract around semantic-control outcomes. ErrWouldBlock and ErrMore remain non-failure control outcomes; callers should process the returned count first, then use helpers such as IsMore, IsWouldBlock, IsSemantic, IsNonFailure, IsFailure, or Classify to choose the next control action.

The package now exposes IsFailure(err error) bool for the direct failure case: it reports true only for actual failures, not for nil, ErrWouldBlock, or ErrMore. The documentation also separates wrapped semantic classification at API boundaries from exact sentinel identity used by internal hot-path policy dispatch.

Usage

n, err := op()
if n > 0 {
	consume(n)
}

switch {
case err == nil:
	return nil
case iox.IsMore(err):
	continue
case iox.IsWouldBlock(err):
	wait()
	continue
default:
	return err
}

Highlights

The main change is clearer public vocabulary for the four observable outcomes: terminal success, live-frontier continuation, readiness suspension, and actual failure. IsFailure gives callers a direct helper for the failure case when they need it, while the canonical loop can continue to branch on IsMore, IsWouldBlock, and ordinary error return.

The GoDoc and README guidance now consistently say to prefer iox semantic helper predicates for ordinary classification. Wrapped semantic sentinels still classify correctly through the helpers, while internal copy and tee policy dispatch intentionally depends on exact unwrapped sentinels for hot-path retry decisions.

What's Changed

Features

  • Add IsFailure(err error) bool to classify actual failures directly without treating ErrWouldBlock or ErrMore as failures (#27)

Documentation

  • Clarify semantic-control boundaries in package GoDoc, including count-first handling, bounded CopyN terminal behavior, helper-based classification, and exact sentinel dispatch for hot paths (#27)

Tests

  • Add regression coverage for wrapped ErrMore classification and for policy dispatch returning wrapped semantic sentinels without exact-sentinel retry (#27)

Compatibility

  • Go: 1.26+
  • Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/loong64

iox v0.3.5 — Clarify copy fast-path contracts

04 May 05:44
Immutable release. Only release title and notes can be modified.
db4ea68

Choose a tag to compare

Introduction

iox provides non-blocking I/O semantics for Go, extending the standard io package with explicit control signals for readiness suspension and live streaming or multishot frontiers while preserving ordinary io.Reader and io.Writer compatibility.

Blocking I/O code often treats (count, error) as a simple success or failure boundary. Non-blocking and streaming I/O also need to expose "not ready yet" and "this operation is still alive." iox makes both cases explicit: ErrWouldBlock means the immediate operation reached a no-progress readiness boundary and should be retried only after external readiness or event evidence, while ErrMore means the current observation reached a live stream frontier and the same logical operation can yield later observations before it terminates, is canceled, or fails.

The package defines two semantic errors: ErrWouldBlock for guarded retry suspension, and ErrMore for operation-still-alive stream or multishot control. Classification helpers such as IsSemantic, IsWouldBlock, IsMore, IsProgress, IsNonFailure, and Classify keep this control flow explicit without relying on string matching.

The Copy family preserves semantic errors at the same (count, error) boundary used by standard Go I/O. Generic iox read/write loops provide seeker rollback for partial semantic writes, while delegated io.WriterTo and io.ReaderFrom fast paths remain available and own their own source advancement, retry safety, semantic-error preservation, and progress accounting.

Usage

n, err := iox.Copy(dst, src)
if n > 0 {
    // Account for the delivered prefix before following the control signal.
}
switch {
case errors.Is(err, iox.ErrWouldBlock):
    // The current attempt would block; wait for readiness before retrying.
case errors.Is(err, iox.ErrMore):
    // The operation frontier is still live; keep observing for the next result.
case err != nil:
    // Terminal failure.
}

Highlights

This release clarifies the fast-path boundary for Copy and CopyPolicy: when io.WriterTo or io.ReaderFrom is selected, that delegated implementation owns its own source advancement, partial-write recovery, semantic-error preservation, retry safety, and progress accounting. The generic iox.Copy rollback guarantee still applies to the read/write loop owned by iox; fast paths must make retry safe for bytes not reported as transferred.

For callers, the important semantic split is precise: ErrWouldBlock is a no-progress readiness suspension, while ErrMore is streaming or multishot live-frontier control. ErrWouldBlock is not delayed failure, not terminal success, and not stream liveness; it preserves retry authority until readiness, cancellation, closure, or failure changes the operation state. ErrMore is not terminal success, not failure, and not proof by itself that bytes were delivered; it may accompany a positive count, but the operation-still-alive semantics are the point of the signal.

In aggregate copy results, a positive written count with ErrWouldBlock means earlier loop steps transferred bytes before the current attempt reached the readiness boundary. Callers should account for the delivered prefix, then wait for readiness before retrying the remaining work.

What's Changed

Improvements

  • Clarify Copy and CopyPolicy fast-path contracts for io.WriterTo and io.ReaderFrom, including ownership of source advancement, partial-write recovery, semantic-error preservation, retry safety, and progress accounting (#25)
  • Replace stale stack-buffer wording with implementation-accurate internal fixed-size buffer wording across GoDocs and README files (#25)

Tests

  • Update slow-path copy benchmarks so they force the generic read/write loop instead of selecting io.WriterTo (#25)

CI and Tooling

  • Add a 10-minute GitHub Actions job timeout while keeping the intentional 120-second Go test timeout in CI and Makefile validation commands (#25)
  • Update GitHub Actions major versions for checkout, setup-go, and Codecov (#25)

Compatibility

  • Go: 1.26+
  • Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/loong64

iox v0.3.4 — Fix ErrUnexpectedEOF contract for policy copy functions

26 Feb 12:19
a37d5e5

Choose a tag to compare

Introduction

iox provides non-blocking I/O semantics for Go, extending the standard io package with first-class signals for would-block and partial-completion states.

In traditional blocking I/O, operations either succeed or fail. Non-blocking I/O introduces a third state: "would block" — the operation cannot proceed right now but isn't a failure. Additionally, streaming operations may return "more" — progress was made, but the operation isn't finished. The iox package treats these states as control flow signals, not errors.

The package defines two semantic errors: ErrWouldBlock (no progress, retry later) and ErrMore (progress made, more data available).

The Copy family of functions (Copy, CopyBuffer, CopyN, CopyBufferPolicy) preserve non-blocking semantics that io.Copy would swallow. They support WriterTo/ReaderFrom fast paths and implement seeker rollback for partial writes, enabling safe retry without data loss.

Usage

n, err := iox.CopyNPolicy(dst, src, 1024, policy)
if errors.Is(err, io.ErrUnexpectedEOF) {
    // Source had fewer than 1024 bytes — short copy
}

Highlights

CopyNPolicy and CopyNBufferPolicy now correctly return io.ErrUnexpectedEOF when the source provides fewer bytes than requested, matching the contract established by CopyN and CopyNBuffer. The slow-path write loop in copyBufferPolicy also gains rollback on wrapped semantic write errors, preventing potential data loss on seekable readers.

What's Changed

Bug Fixes

  • CopyNPolicy and CopyNBufferPolicy now return io.ErrUnexpectedEOF on short copies instead of (written, nil) — aligns with CopyN/CopyNBuffer contract (#23)
  • copyBufferPolicy slow-path write loop now performs seeker rollback on wrapped semantic write errors, consistent with copyBuffer (#23)

Tests

  • Add coverage tests for CopyNPolicy, CopyNBufferPolicy error passthrough, and copyBufferPolicy wrapped semantic error rollback (#23)

Compatibility

  • Go: 1.26+
  • Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/loong64

iox v0.3.3 — Go 1.26 modernization

16 Feb 13:40
cd796d5

Choose a tag to compare

Introduction

iox extends Go's standard io package with non-blocking semantics for event-driven I/O stacks. It introduces two first-class control flow signals — ErrWouldBlock (no progress possible now; retry after readiness) and ErrMore (progress happened; more completions will follow) — while preserving standard io.Reader/io.Writer compatibility and fast paths (WriterTo/ReaderFrom).

The package provides Copy/CopyN helpers with semantic policy support, Tee adapters with precise progress counting, adaptive backoff for I/O readiness waiting, and Seeker-based partial write recovery. All semantic predicates and policy dispatch paths are zero-allocation.

Usage

var b iox.Backoff

for {
    n, err := conn.Read(buf)
    if err == iox.ErrWouldBlock {
        b.Wait()
        continue
    }
    if err != nil {
        return err
    }
    process(buf[:n])
    b.Reset()
}

Highlights

This release bumps the minimum Go version to 1.26 and modernizes the codebase using go fix. No API changes — fully backward compatible with v0.3.x.

What's Changed

Improvements

  • Bump minimum Go version from 1.25 to 1.26 (#21)
  • Apply go fix modernizers: min()/max() builtins, for range N syntax (#21)
  • CI matrix updated to test against Go 1.26.x and stable (#21)

Compatibility

  • Go: 1.26+
  • Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/loong64

iox v0.3.2 — Build tooling and benchmark suite

05 Feb 04:15
4500681

Choose a tag to compare

Introduction

iox provides non-blocking I/O semantics for Go, extending the standard io package with first-class signals for would-block and partial-completion states.

  • In traditional blocking I/O, operations either succeed or fail. Non-blocking I/O introduces a third state: "would block" — the operation cannot proceed right now but isn't a failure.

  • Additionally, streaming operations may return "more" — progress was made, but the operation isn't finished. The iox package treats these states as control flow signals, not errors.

  • The package defines two semantic errors: ErrWouldBlock (no progress, retry later) and ErrMore (progress made, more data available). Classification functions like IsSemantic(), IsWouldBlock(), IsMore(), IsProgress(), and IsNonFailure() enable clean branching logic without string comparisons or type assertions.

  • The Copy family of functions (Copy, CopyBuffer, CopyN, CopyBufferPolicy) preserve non-blocking semantics that io.Copy would swallow. They support WriterTo/ReaderFrom fast paths and implement seeker rollback for partial writes, enabling safe retry without data loss.

Usage

// Semantic error classification
n, err := reader.Read(buf)
switch {
case err == nil:
    // Complete success
case iox.IsWouldBlock(err):
    // No progress now, retry later
case iox.IsMore(err):
    // Progress made, more data available
default:
    // Actual failure
}

// Adaptive backoff for non-blocking I/O
var backoff iox.Backoff
for {
    n, err := conn.Read(buf)
    if iox.IsWouldBlock(err) {
        backoff.Wait()
        continue
    }
    backoff.Reset()
    // process data...
}

Highlights

This release adds build tooling with Makefile targets and a comprehensive benchmark suite for performance analysis.

What's Changed

Build & Tooling

  • Add Makefile with targets: test, bench, bench-cpu, bench-mem, vet, clean

Documentation

  • Add Copilot review instructions for iox semantics

Improvements

  • Extract rollbackSeeker() helper to reduce code duplication in Copy functions

CI/CD

  • Add Go stable to test matrix

Compatibility

  • Go: 1.25+
  • Platforms: All Go-supported platforms

iox v0.3.1 — non-blocking io semantics extensions

10 Jan 07:48
642b1bd

Choose a tag to compare

iox extends Go's io package with non-blocking and multi-shot I/O semantics.

It introduces two control flow signals:

  • ErrWouldBlock — no progress possible now; retry after waiting
  • ErrMore — progress made; more completions will follow

These are not failures — they're normal control flow for event-driven systems.

The package provides Copy, CopyN, TeeReader, TeeWriter helpers that preserve these semantics through fast paths (WriterTo/ReaderFrom). Classification functions (IsSemantic, IsNonFailure, IsProgress) and policy-based retry (SemanticPolicy) enable flexible integration with event loops and io_uring-style completion models.

What's Changed

Maintenance

  • Add blank lines between type declarations and methods in tests (#11)
  • Add Copilot custom instructions for semantic error patterns (#12)
  • Switch coverage reporting from Coveralls to Codecov (#13)

iox v0.3.0 — Adaptive back-off for external I/O waiting

22 Dec 03:51
90db967

Choose a tag to compare

Added

  • iox.Backoff: Adaptive back-off for external I/O waiting. Zero-value is ready-to-use with sensible defaults (500µs base, 100ms max). Implements linear block-based scaling with ±12.5% jitter to prevent thundering herds. Part of the Three-Tier Progress model: Strike (syscall) → Spin (atomic) → Adapt (I/O readiness).

iox v0.2.2 — Partial Write Safety

18 Dec 05:54
5029f1e

Choose a tag to compare

🚀 Features & Improvements

  • Partial Write Recovery: iox.Copy now attempts to rewinds the source read pointer using io.Seeker if a destination write blocks mid-chunk.
  • Fail-Fast for Non-Seekable Streams: Added ErrNoSeeker. Copy operations now return this error if unwritten bytes cannot be recovered from the transient buffer, preventing silent data corruption.

📚 Documentation

  • Updated GoDocs for the Copy family to clarify the "Destructive Read" contract.
  • Comprehensive updates to READMEs regarding non-blocking data integrity.

⚠️ Breaking Changes

  • iox.Copy and related helpers will now return ErrNoSeeker on non-seekable streams if a partial write occurs during a semantic error. To maintain delivery guarantees on non-seekable streams, use CopyPolicy with a retry-aware policy.

iox v0.2.1 — Fix Tee adapters report primary progress and mirror accepted bytes

15 Dec 16:45
1b99616

Choose a tag to compare

Summary

This release tightens iox tee adapter semantics to be safer under partial progress and non-blocking control-flow errors, while staying compatible with Go’s io.Reader/io.Writer conventions.

Behavioral changes

  • TeeReader / TeeReaderPolicy
    • Returned n now always equals bytes consumed from the source (r.Read count), even if the side write fails, is short, or returns ErrWouldBlock/ErrMore.
    • Rationale: avoids “byte loss” at the Read boundary; callers can safely process p[:n] even when err != nil.
  • TeeWriter / TeeWriterPolicy
    • Returned n now always equals bytes accepted by the primary writer.
    • Rationale: makes retry-by-slicing (p[n:]) safe and prevents duplicating primary writes if the tee fails.
  • TeeWriterPolicy now mirrors to tee incrementally as primary accepts bytes, ensuring tee observes the accepted prefix even when primary returns a semantic boundary after partial progress.

Documentation

  • Updated TeeWriter GoDoc to precisely describe call order, error precedence, and the adapter’s count semantics.
  • READMEs include a “Tee semantics (counts and errors)” section:
    • n is progress (source/primary), and (n > 0, err) is a valid outcome.
    • For best interoperability with policy-driven helpers, prefer returning ErrWouldBlock/ErrMore as sentinels (avoid wrapping).

Tests

  • Updated and expanded tee-related tests to assert the new count semantics and boundary/error propagation.

Compatibility notes

  • Callers should follow the standard pattern: use progress first, then handle err:
    • Read: if n > 0, process p[:n] even when err != nil.
    • Write: if n > 0, treat p[:n] as accepted by primary even when err != nil.
  • In policy-driven hot loops, semantic detection is optimized for sentinel comparison (err == ErrWouldBlock / err == ErrMore); wrapped semantic errors may not be treated as semantic for policy decisions.

iox v0.2.0 — Semantic Policy for would-block and multi-shot I/O

14 Dec 12:08
f55062c

Choose a tag to compare

iox v0.2.0

This release adds Semantic Policy support: an opt-in way to decide what helpers should do when they encounter non-blocking control-flow signals.

If you want pure non-blocking semantics, keep using the existing APIs (or pass nil policy): helpers return ErrWouldBlock / ErrMore to the caller as before.

If you want a more “engine-like” mode, policies can yield and retry on semantic signals (e.g., map ErrWouldBlock to “wait then retry”) while still preserving iox’s contracts.

Added

Policy-aware helpers (opt-in)

  • Copy:
    • CopyPolicy
    • CopyBufferPolicy
    • CopyNPolicy
    • CopyNBufferPolicy
  • Tee:
    • TeeReaderPolicy
    • TeeWriterPolicy

Semantic Policy API

  • SemanticPolicy interface to control:
    • how to react to ErrWouldBlock / ErrMore
    • how to yield/wait before retrying
  • Ready-to-use policies:
    • ReturnPolicy (never retries; preserves non-blocking behavior)
    • YieldPolicy (retry on would-block; return on more)
    • YieldOnWriteWouldBlockPolicy (retry only on writer-side would-block)
  • PolicyFunc for lightweight customization without defining a new type.
  • Op labels to identify where a semantic signal originated (read vs write, copy vs tee, fast paths, etc.).

Semantics utilities

  • Compact classification helpers for state machines and switches:
    • Outcome + Classify(err)
  • Convenience predicates remain available and useful with policies:
    • IsNonFailure, IsWouldBlock, IsMore, IsProgress

Behavior notes

  • Default behavior is unchanged: passing nil policy means helpers return semantic signals immediately (no waiting, no retries).
  • Policy-aware variants may retry internally when instructed; ensure your Yield hook actually waits/parks/polls as appropriate to avoid spinning.
  • Semantic propagation and partial progress are preserved: helpers may still report (n > 0, ErrWouldBlock) or (n > 0, ErrMore) when returning to the caller.

Upgrade notes

  • No breaking API changes expected for existing users.
  • If you previously implemented your own retry/yield loop around iox.Copy / Tee*, you can now centralize that logic via a SemanticPolicy.