Releases: hayabusa-cloud/iox
iox v0.3.6 — Clarify semantic control classification
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) boolto classify actual failures directly without treatingErrWouldBlockorErrMoreas failures (#27)
Documentation
- Clarify semantic-control boundaries in package GoDoc, including count-first handling, bounded
CopyNterminal behavior, helper-based classification, and exact sentinel dispatch for hot paths (#27)
Tests
- Add regression coverage for wrapped
ErrMoreclassification 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
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
CopyandCopyPolicyfast-path contracts forio.WriterToandio.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
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
CopyNPolicyandCopyNBufferPolicynow returnio.ErrUnexpectedEOFon short copies instead of(written, nil)— aligns withCopyN/CopyNBuffercontract (#23)copyBufferPolicyslow-path write loop now performs seeker rollback on wrapped semantic write errors, consistent withcopyBuffer(#23)
Tests
- Add coverage tests for
CopyNPolicy,CopyNBufferPolicyerror passthrough, andcopyBufferPolicywrapped 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
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 fixmodernizers:min()/max()builtins,for range Nsyntax (#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
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
ioxpackage treats these states as control flow signals, not errors. -
The package defines two semantic errors:
ErrWouldBlock(no progress, retry later) andErrMore(progress made, more data available). Classification functions likeIsSemantic(),IsWouldBlock(),IsMore(),IsProgress(), andIsNonFailure()enable clean branching logic without string comparisons or type assertions. -
The
Copyfamily of functions (Copy,CopyBuffer,CopyN,CopyBufferPolicy) preserve non-blocking semantics thatio.Copywould swallow. They supportWriterTo/ReaderFromfast 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
stableto test matrix
Compatibility
- Go: 1.25+
- Platforms: All Go-supported platforms
iox v0.3.1 — non-blocking io semantics extensions
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 waitingErrMore— 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
iox v0.3.0 — Adaptive back-off for external I/O waiting
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
🚀 Features & Improvements
- Partial Write Recovery:
iox.Copynow attempts to rewinds the source read pointer usingio.Seekerif 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
Copyfamily to clarify the "Destructive Read" contract. - Comprehensive updates to READMEs regarding non-blocking data integrity.
⚠️ Breaking Changes
iox.Copyand related helpers will now returnErrNoSeekeron non-seekable streams if a partial write occurs during a semantic error. To maintain delivery guarantees on non-seekable streams, useCopyPolicywith a retry-aware policy.
iox v0.2.1 — Fix Tee adapters report primary progress and mirror accepted bytes
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
nnow always equals bytes consumed from the source (r.Readcount), even if the side write fails, is short, or returnsErrWouldBlock/ErrMore. - Rationale: avoids “byte loss” at the
Readboundary; callers can safely processp[:n]even whenerr != nil.
- Returned
TeeWriter/TeeWriterPolicy- Returned
nnow 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.
- Returned
TeeWriterPolicynow mirrors toteeincrementally as primary accepts bytes, ensuringteeobserves the accepted prefix even when primary returns a semantic boundary after partial progress.
Documentation
- Updated
TeeWriterGoDoc to precisely describe call order, error precedence, and the adapter’s count semantics. - READMEs include a “Tee semantics (counts and errors)” section:
nis progress (source/primary), and(n > 0, err)is a valid outcome.- For best interoperability with policy-driven helpers, prefer returning
ErrWouldBlock/ErrMoreas 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: ifn > 0, processp[:n]even whenerr != nil.Write: ifn > 0, treatp[:n]as accepted by primary even whenerr != 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
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:
CopyPolicyCopyBufferPolicyCopyNPolicyCopyNBufferPolicy
- Tee:
TeeReaderPolicyTeeWriterPolicy
Semantic Policy API
SemanticPolicyinterface to control:- how to react to
ErrWouldBlock/ErrMore - how to yield/wait before retrying
- how to react to
- 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)
PolicyFuncfor lightweight customization without defining a new type.Oplabels 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
nilpolicy means helpers return semantic signals immediately (no waiting, no retries). - Policy-aware variants may retry internally when instructed; ensure your
Yieldhook 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 aSemanticPolicy.