Skip to content

Releases: hayabusa-cloud/uring

uring v0.0.0-beta2 — Pure Go io_uring library for Linux 6.18+

15 May 18:44
Immutable release. Only release title and notes can be modified.
b999721

Choose a tag to compare

Introduction

Package uring is a minimal, pure Go io_uring library for Linux 6.18+. It creates and starts rings, prepares submission queue entries, reaps completion queue entries, and carries request identity through the kernel's 64-bit user_data field. On top of that it provides registered file and buffer support, provided buffer rings, multishot operations, zero-copy send and receive, multicast helpers, and listener-setup primitives.

The design keeps the boundary established in the earlier alpha and beta releases: kernel-facing mechanics and completion facts live at the API surface, while retry, parking, routing, and orchestration stay in higher layers. There is no built-in event loop, scheduler, or goroutine management. In the dependency graph uring still sits between the foundation packages (zcall, iox, iofd, iobuf, sock, spin, dwcas, framer) and the proactor or runtime layers above.

The hot submit-and-reap paths remain zero-allocation. SQEContext keeps packing submission metadata into the 64-bit user_data field that the kernel round-trips in every CQE, with Direct, Indirect, and Extended modes covering the common path and wider payloads without heap allocation. beta2 is a focused correctness and ergonomics release on top of v0.0.0-beta: it makes IOPOLL completions visible from the public wait surface, hardens the multishot subscription lifecycle around cancel races, and adds small completion-error helpers so caller-side runtimes can decode CQE results without duplicating the -errno mapping.

Usage

Drive completion reaping explicitly and keep retry policy in caller code:

ring, err := uring.New(func(o *uring.Options) {
    o.Entries = uring.EntriesMedium
})
if err != nil {
    return err
}
if err := ring.Start(); err != nil {
    return err
}
defer ring.Stop()

fd := int32(file.Fd())
buf := make([]byte, 4096)
ctx := uring.PackDirect(uring.IORING_OP_READ, 0, 0, fd)
if err := ring.Read(ctx, buf); err != nil {
    return err
}

cqes := make([]uring.CQEView, 64)
var backoff iox.Backoff

for {
    n, err := ring.Wait(cqes)
    switch iox.Classify(err) {
    case iox.OutcomeWouldBlock:
        backoff.Wait()
        continue
    case iox.OutcomeFailure:
        return err
    }
    if n == 0 {
        backoff.Wait()
        continue
    }

    backoff.Reset()
    for i := range n {
        cqe := cqes[i]
        if cqe.Op() != uring.IORING_OP_READ || int32(cqe.FD()) != fd {
            continue
        }
        if err := cqe.Err(); err != nil {
            return fmt.Errorf("uring read failed: %w", err)
        }
        handle(buf[:int(cqe.Res)])
        return nil
    }
}

Highlights

v0.0.0-beta2 is a hardening and ergonomics release for the standalone uring package. It keeps the v0.0.0-beta surface and boundary contract, exposes a small completion-error helper family on CQEView, DirectCQE, and ExtCQE, gives MultishotSubscription an explicit HandleCQE dispatch entry point, and makes IOPOLL ring waits observe completions through a nonblocking poll enter at the empty-CQ boundary instead of returning early with no observable progress.

Architecture Continuity

The same core model from v0.0.0-beta remains in place:

  • Uring, SQEContext, CQEView, ContextPools, ExtSQE, ListenerOp, MultishotSubscription, ZCTracker, and budget-aware setup helpers stay the same explicit carriers.
  • Direct, indirect, and extended submission modes still cover the zero-allocation common path and wider payload cases.
  • Multishot, provided-buffer, listener, and zero-copy oriented APIs remain part of the package surface.
  • iox outcomes (nil, iox.ErrWouldBlock, iox.ErrMore, real errors) and CQE facts (Res, Flags, IORING_CQE_F_MORE, IORING_CQE_F_BUF_MORE) are still preserved at the boundary.
  • Submit, Wait, Stop, and ResizeRings stay caller-serialized on default single-issuer rings; the shared-submit lock applies only on MultiIssuers=true rings.
  • Scheduling and retry policy still belong to callers, not to uring itself.

What's Changed

Bug Fixes

  • Make Wait, WaitDirect, and WaitExtended on IORING_SETUP_IOPOLL rings perform a nonblocking poll enter at the empty-CQ boundary so polled completions become visible to caller code instead of being reported as no observable progress.
  • Route multishot CQEs through the owning MultishotSubscription and resolve the subscription's ExtSQE from the CQE's own user_data so a stale or already-retired subscription cannot consume a CQE that belongs to a different live route.
  • Guard multishot cancel submission against ExtSQE reuse with a cancelSubmit ownership flag, finalize retirement only after the cancel SQE has been published and the terminal CQE observed, and stop multiple terminal paths from racing to retire the same subscription twice.

Features

  • Add CompletionError(res int32) error plus (*CQEView).Err(), (*DirectCQE).Err(), and (*ExtCQE).Err() so caller-side code can decode a CQE result into the package error model without duplicating the kernel -errno translation.
  • Add (*MultishotSubscription).HandleCQE(CQEView) bool for caller-side dispatch loops that want to route a copied CQE back to its owning subscription without exposing internal pool layout; it returns false for non-extended CQEs, foreign routes, and observations whose current pooled ExtSQE owner is no longer the target subscription.

Improvements

  • Bump foundation dependencies (dwcas, framer, iobuf, iofd, iox, sock, spin, zcall) to current minor versions and keep the module hot paths zero-allocation and inline-friendly.
  • Split the submit enter fast path into wakeSQPollLocked and flushSubmitLocked so the SQPOLL wakeup and the actual flush keep clear lifetime boundaries under the submit-state lock, and tighten the closed-ring guard.
  • Tighten the MultishotSubscription finalize/retire sequence so terminal CQEs and cancel completion can race without double-retiring the pooled ExtSQE.
  • Improve test workflow ergonomics: reduce the RLIMIT_MEMLOCK and provided-buffer pressure that unit tests place on shared CI runners and add deterministic race subsets and a public benchmark workflow under the existing CI surface.

Platform Support

  • Linux 6.18+ remains the primary runtime target.
  • The package continues to build for multiple Linux architectures and keeps a Darwin build surface for non-Linux development and cross-build checks.

Documentation

  • Refresh README files across en, es, fr, ja, and zh-CN for consistency, reformat the public package guidance, and add benchmark badges.
  • Update package-level GoDoc and inline comments around CQEView lifetime, the multishot subscription lifecycle, single-issuer Cancel/Unsubscribe serialization, IOPOLL Wait* behavior, and the new completion-error helpers.

Compatibility

  • Go: 1.26+
  • Platforms: Linux 6.18+

uring v0.0.0-beta — Minimal pure Go io_uring library for Linux 6.18+

27 Apr 09:04
26b4935

Choose a tag to compare

Introduction

uring is a low-level Go package for building directly on Linux io_uring without hiding the kernel boundary behind a runtime framework. It creates and starts rings, prepares submission queue entries, reaps completion queue entries, carries request identity through the 64-bit user_data field, and exposes completion results and flags in the form caller-side runtimes need for their own dispatch logic.

uring is intentionally shaped as a precise boundary layer. uring owns SQE encoding, CQE observation, registered-buffer lifetimes, file-descriptor domain crossings, multishot liveness flags, zero-copy notification accounting, and explicit kernel capability checks. It does not decide when to retry, park, batch, route a handler, advance a session, cancel a higher-level protocol, or finally retire application resources; those policies belong above the package.

The public semantics keep four outcomes distinct: nil means a step completed, iox.ErrWouldBlock means no observable progress is available now, iox.ErrMore means progress happened and the operation remains live, and any other error is a real failure. At the CQE boundary, negative res values remain kernel -errno results, IORING_CQE_F_MORE remains continuation evidence, and empty queues or saturated queues remain visible facts rather than being folded into convenience behavior.

The API is built from a small set of explicit carriers: Uring, SQEContext, CQEView, ContextPools, ExtSQE, ListenerOp, MultishotSubscription, ZCTracker, and budget-aware setup helpers. They stay close to the Linux ABI while giving Go callers typed descriptors, reusable context pools, and clear ownership rules.

Usage

The example below submits a read operation and then handles the matching completion. It assumes file is an open *os.File and handle consumes the bytes read; the wait loop uses iox.Classify so control-flow errors are handled by their semantic outcome.

ring, err := uring.New(func(o *uring.Options) {
    o.Entries = uring.EntriesMedium
})
if err != nil {
    return err
}
if err := ring.Start(); err != nil {
    return err
}
defer ring.Stop()

fd := int32(file.Fd())
buf := make([]byte, 4096)
ctx := uring.PackDirect(uring.IORING_OP_READ, 0, 0, fd)
if err := ring.Read(ctx, buf); err != nil {
    return err
}

cqes := make([]uring.CQEView, 64)
var backoff iox.Backoff

for {
    n, err := ring.Wait(cqes)
    switch iox.Classify(err) {
    case iox.OutcomeWouldBlock:
        backoff.Wait()
        continue
    case iox.OutcomeFailure:
        return err
    }
    if n == 0 {
        backoff.Wait()
        continue
    }

    backoff.Reset()
    for i := range n {
        cqe := cqes[i]
        if cqe.Op() != uring.IORING_OP_READ || int32(cqe.FD()) != fd {
            continue
        }
        if cqe.Res < 0 {
            return fmt.Errorf("uring read failed: res=%d", cqe.Res)
        }
        handle(buf[:int(cqe.Res)])
        return nil
    }
}

Highlights

uring exposes a broad Linux 6.18+ io_uring surface while keeping the package's promise narrow and predictable: submit work, observe completions, preserve identity, and leave policy to the caller. It includes ring lifecycle management, direct and extended user_data contexts, provided buffers and incremental receive, multishot accept and receive paths, file and socket operation wrappers, zero-copy send tracking through ZCTracker, listener setup helpers, budget-aware buffer sizing, and additive ZCRX scaffolding for environments that support it.

Boundary Contract

uring owns the facts that cross the kernel boundary: SQE fields, CQE res and flags, packed SQEContext, borrowed CQEView, registered-buffer lifetime, provided-buffer selection, multishot terminal ownership, zero-copy notification state, and unsupported-feature errors. Caller-side code owns completion correlation to continuations, retry and backoff after iox.ErrWouldBlock, routing into handlers or sessions, batching, parking, cancellation races, connection lifecycle, and final release ordering.

This split is the core design. A completion can report progress, no progress, continuation, notification, cancellation, timeout, or -errno; uring preserves that observation and lets the layer above decide what it means for the application protocol.

What's Included

Ring and context lifecycle

  • Uring.New, Start, Stop, Wait, ResizeRings, SQAvailable, CQPending, and RingFD form the live ring surface; teardown is caller-serialized, and Stop() is idempotent.
  • SQEContext supports direct, indirect, and extended modes so common completions stay compact while advanced callers can borrow full ExtSQE storage; SQEContext.OrFlags additively combines caller-selected SQE flags without clobbering wrapper-set bits.
  • ContextPools, IndirectSQE, and ExtSQE provide reusable context storage, with sidecar anchors keeping live Go references for owners, handlers, and listener sockaddrs adjacent to pooled ExtSQE slots.

Operations

  • Socket, connection, send/receive, file I/O, file management, xattr, splice, tee, pipe, timeout, cancel, poll, futex, waitid, message-ring, fixed-file, files-update, and command wrappers are exposed close to their kernel SQE shape.
  • Nop128 and UringCmd128 support 128-byte SQE layouts and return ErrNotSupported when the ring or kernel cannot support that path.

Buffers and memory

  • Fixed provided buffers and multi-size buffer groups cover common receive provisioning strategies.
  • OptionsForBudget, OptionsForSystem, and BufferConfigForBudget size ring entries, registered-buffer memory, and default buffer-group scale from an explicit memory budget.

Multishot, listener, and zero-copy notifications

  • Raw multishot submit methods and helper-backed subscriptions preserve the live-operation distinction carried by IORING_CQE_F_MORE, and the subscription typestate is Active → Cancelling → Stopped, with Stopped absorbing.
  • ZCTracker models the two-CQE zero-copy send lifecycle: operation completion first, then notification when the buffer can be reused, with a single-fire affine guard on the completion callback.
  • Listener setup helpers let caller-side code advance socket, bind, listen, readiness, and accept from observed CQEs without turning setup into hidden runtime policy; ListenerOp.Close is caller-serialized and finalizes only after the pending listener-setup CQE is drained.

Capability and ZCRX boundary

  • The default low-overhead configuration requests IORING_SETUP_SINGLE_ISSUER plus IORING_SETUP_DEFER_TASKRUN; opting into MultiIssuers=true switches to a IORING_SETUP_COOP_TASKRUN path and pays its own submit-state synchronization cost.
  • ZCRX types, refill helpers, area registration, Linux 6.19+ register/query entry points, and corresponding capability errors document the zero-copy receive boundary and required NIC/kernel shape; this surface is additive opt-in rather than a portable receive abstraction.

Caller-owned safety

uring does not add heap allocation, mutexes, or extra syscalls to make caller mistakes safe on hot paths. Raw ExtSQE.UserData overlays are caller-beware storage and the GC does not trace them; buffers and iovecs must remain valid until their CQE arrives; Stop() and Close() require the caller to drain in-flight work, reap outstanding CQEs, and quiesce live subscriptions before final teardown; provided-buffer ring slots transfer ownership to the kernel on provide; and CQE-side completion lifecycle, GC rooting, and concurrent retirement belong in the caller-side completion layer above uring.

Compatibility

  • Go: 1.26+
  • Runtime target: Linux 6.18+

uring v0.0.0-alpha2 — Pure Go io_uring alpha2 for Linux 6.18+

14 Apr 07:08
f0bfccd

Choose a tag to compare

Introduction

uring is a minimal, pure Go io_uring library for Linux 6.18+. It handles
ring setup and teardown, SQE preparation, CQE decoding, and identity transport
through the kernel's 64-bit user_data field. On top of that it provides
buffer registration, provided buffer rings, multishot operations, zero-copy
send/receive, multicast helpers, and listener-setup primitives.

The design keeps the same boundary established in the initial alpha release:
kernel-facing mechanics and completion facts live at the API surface, while
scheduling, retry, and orchestration stay in higher layers. There is no built-
in event loop, scheduler, or goroutine management. In the dependency graph
uring still sits between the foundation packages (zcall, iox, iofd,
iobuf, sock, spin, dwcas, framer) and the proactor/runtime layers
above.

The hot submit-and-reap paths remain zero-allocation. SQEContext still packs
submission metadata into the 64-bit user_data field that the kernel round-
trips in every CQE. This alpha2 release keeps that model intact while
hardening wrapper correctness, lifecycle documentation, and release readiness.
The result is a stronger alpha baseline for callers building runtimes,
dispatchers, or protocol engines on top of uring.

Usage

Drive completion reaping explicitly and keep retry policy in caller code:

ring, err := uring.New(func(o *uring.Options) {
    o.Entries = uring.EntriesMedium
})
if err != nil {
    return err
}
if err := ring.Start(); err != nil {
    return err
}
defer ring.Stop()

cqes := make([]uring.CQEView, 64)
var backoff iox.Backoff

for {
    n, err := ring.Wait(cqes)
    if errors.Is(err, iox.ErrWouldBlock) {
        backoff.Wait()
        continue
    }
    if err != nil {
        return err
    }
    if n == 0 {
        backoff.Wait()
        continue
    }

    backoff.Reset()
    for i := range n {
        cqe := cqes[i]
        if cqe.Res < 0 {
            return fmt.Errorf("completion failed: op=%d fd=%d res=%d", cqe.Op(), cqe.FD(), cqe.Res)
        }
        fmt.Printf("completed op=%d on fd=%d with res=%d\n", cqe.Op(), cqe.FD(), cqe.Res)
    }
    break
}

Highlights

v0.0.0-alpha2 is a hardening release for the standalone uring package. It
keeps the alpha1 surface area and design boundary, but improves the contract at
the package edge: wrapper APIs now preserve caller-selected SQE flags, direct-
slot/fixed-file paths behave correctly, lifecycle and teardown rules are more
explicit, and the current Linux-facing surface has been revalidated with broad
package tests and cross-build checks.

Architecture Continuity

The same core model from alpha1 remains in place:

  • SQEContext is still the submission identity token and continues to round-
    trip operation metadata through user_data.
  • Direct, indirect, and extended submission modes still cover the common
    zero-allocation path plus the wider payload cases.
  • Multishot, provided-buffer, listener, and zero-copy oriented APIs remain part
    of the package surface.
  • Scheduling and retry policy still belong to callers, not to uring itself.

What's Changed

Bug Fixes

  • Preserve caller SQE flags across convenience wrappers so direct-slot and
    fixed-file operations keep IOSQE_FIXED_FILE and related caller-selected
    bits intact.
  • Correct the wrapper-layer flag merge behavior for connect-style paths, fixing
    a real direct-socket failure mode that could surface as -ENOTSOCK when a
    registered-file slot was misinterpreted as a raw file descriptor.

Features

  • Add SQEContext.OrFlags as an additive flag combinator for callers and
    wrapper code that must extend existing SQE flags without clobbering them.

Improvements

  • Tighten lifecycle documentation around Start/Stop, Close,
    multishot retirement, and listener setup so caller-owned teardown rules are
    explicit at the package boundary.
  • Improve poll-related integration and example coverage so tests follow the
    package's real wait/enter contract instead of timing guesses.
  • Keep the hot flag-composition path zero-allocation and inline-friendly while
    fixing wrapper correctness.

Compatibility

  • Go: 1.26+
  • Platforms: Linux 6.18+

uring v0.0.0-alpha1 — Pure Go io_uring for Linux 6.18+

09 Apr 05:17
4bb655b

Choose a tag to compare

Introduction

uring is a minimal, pure Go io_uring library for Linux 6.18+. It handles
ring setup and teardown, SQE preparation, CQE decoding, and identity transport
through the kernel's 64-bit user_data field. On top of that it provides
buffer registration, provided buffer rings, multishot operations, zero-copy
send/receive, multicast helpers, and listener-setup primitives. The package was
extracted from code.hybscloud.com/sox into its own module.

The design draws a clear boundary: kernel-facing mechanics and completion facts
live at the API surface, while scheduling, retry, and orchestration stay in
higher layers. There is no built-in event loop, scheduler, or goroutine
management. In the dependency graph uring sits between the foundation
packages (zcall, iox, iofd, iobuf, sock, spin, dwcas, framer)
and the proactor runtime above.

The hot submit-and-reap paths are zero-allocation. SQEContext packs
submission metadata into the 64-bit user_data field that the kernel
round-trips in every CQE. Three context modes — Direct (inline 64-bit),
Indirect (64-byte pointer), and Extended (128-byte pointer with caller-owned
data) — cover the full range of submission needs without heap allocation on the
common path. The default ring configuration targets the single-issuer fast
path, and budget helpers derive ring and buffer layouts from a memory target or
system class.

Usage

Once submissions are in flight, drive completions with Wait and caller-owned
backoff:

ring, err := uring.New(func(o *uring.Options) {
    o.Entries = uring.EntriesMedium
})
if err != nil {
    return err
}
if err := ring.Start(); err != nil {
    return err
}

cqes := make([]uring.CQEView, 64)
var backoff iox.Backoff

for {
    n, err := ring.Wait(cqes)
    if errors.Is(err, iox.ErrWouldBlock) {
        backoff.Wait()
        continue
    }
    if err != nil {
        return err
    }
    if n == 0 {
        backoff.Wait()
        continue
    }

    backoff.Reset()
    for i := range n {
        cqe := cqes[i]
        if cqe.Res < 0 {
            return fmt.Errorf("completion failed: op=%d fd=%d res=%d", cqe.Op(), cqe.FD(), cqe.Res)
        }
        fmt.Printf("completed op=%d on fd=%d with res=%d\n", cqe.Op(), cqe.FD(), cqe.Res)
    }
    break
}

Highlights

Initial alpha pre-release of the standalone uring package, extracted from sox.
It covers the full io_uring operation surface for the Linux 6.18 baseline:
zero-allocation context transport through three SQEContext modes, multishot
subscriptions with handler and subscriber patterns, multi-size buffer
provisioning, zero-copy send and receive, multicast helpers, and budget-based
ring configuration. Over 50 opcodes span socket, file, transfer, timeout,
cancel, poll, futex, epoll, msg-ring, and uring-cmd categories.

SQEContext

SQEContext is the submission identity token. It packs submission metadata
into the 64-bit user_data field that the kernel round-trips in every CQE.

Mode Representation Typical use
Direct Inline 64-bit payload (op, flags, buf group, fd) Common submit/reap path — zero allocation
Indirect Pointer to IndirectSQE (64 bytes) Full SQE payload when 64 bits are not enough
Extended Pointer to ExtSQE (128 bytes) Full SQE plus 64 bytes of caller-owned user data

Direct mode layout:

┌─────────┬─────────┬──────────────┬────────────────────────────┬────┐
│ Op (8b) │Flags(8b)│ BufGrp (16b) │        FD (30b)            │Mode│
└─────────┴─────────┴──────────────┴────────────────────────────┴────┘
  Bits 0-7  Bits 8-15  Bits 16-31     Bits 32-61              Bits 62-63

Pack a context for submission:

ctx := uring.PackDirect(uring.IORING_OP_RECV, 0, bufferGroupID, clientFD)

Or use the fluent builder:

ctx := uring.ForFD(clientFD).WithOp(uring.IORING_OP_RECV).WithBufGroup(groupID)

When you need caller-owned metadata beyond the 64-bit direct layout, borrow an
ExtSQE:

ext := ring.ExtSQE()
meta := uring.CtxV1Of(ext)
meta.Val1 = requestSeq
sqeCtx := uring.PackExtended(ext)

CQEView

CQEView is the borrowed completion view returned by Wait. It exposes
kernel completion facts — Res, Flags, Op(), FD() — and lets higher
layers decide how to route or interpret them. Recover the original submission
context with Context().

FullSQE() Extended() Mode Available data
false false Direct Op, SQE flags, BufGroup, FD, Res, CQE flags
true false Indirect + full SQE copy
true true Extended + borrowed ExtSQE with caller data

Completion dispatch:

for i := range n {
    cqe := cqes[i]
    if cqe.Res < 0 {
        return fmt.Errorf("completion failed: op=%d fd=%d res=%d", cqe.Op(), cqe.FD(), cqe.Res)
    }
    switch cqe.Op() {
    case uring.IORING_OP_ACCEPT:
        fmt.Printf("accepted fd=%d\n", cqe.Res)
    case uring.IORING_OP_RECV:
        if cqe.HasBuffer() {
            fmt.Printf("buffer id=%d\n", cqe.BufID())
        }
        if cqe.Extended() {
            seq := uring.CtxV1Of(cqe.ExtSQE()).Val1
            fmt.Printf("request seq=%d\n", seq)
        }
    }
}

CQEView, IndirectSQE, ExtSQE, and borrowed buffers must not outlive
their documented lifetimes.

Features

  • Ring lifecycle: New, Start, Stop, Wait, ResizeRings
  • SQEContext with Direct / Indirect / Extended modes for zero-allocation context transport through user_data
  • CQEView with decoded context accessors: Op(), FD(), HasBuffer(), BufID(), HasMore(), FullSQE(), Extended(), Context()
  • Socket operations: TCP, UDP, UDPLITE, SCTP, Unix — with *Direct variants for registered file descriptors
  • File I/O: Read, Write, ReadV, WriteV, ReadFixed, WriteFixed, ReadvFixed, WritevFixed, OpenAt, Close, Sync, Fallocate, FTruncate, Statx, RenameAt, UnlinkAt, MkdirAt, SymlinkAt, LinkAt
  • Multishot operations: AcceptMultishot, ReceiveMultishot, SubmitAcceptMultishot, SubmitAcceptDirectMultishot, SubmitReceiveMultishot, SubmitReceiveBundleMultishot
  • Zero-copy: ReceiveZeroCopy for single-shot zero-copy receive; ZCTracker for two-CQE zero-copy send lifecycle
  • Buffer provisioning: MMAP buffer rings, incremental buffer rings (IOU_PBUF_RING_INC), and multi-size buffer groups with tiered sizing
  • RegisterBufferPool for pooled registered-buffer management
  • File registration: RegisterFiles, RegisterFilesSparse, RegisterFilesUpdate, UnregisterFiles, RegisteredFileCount
  • Buffer registration: RegisterBufRingMMAP, RegisterBufRingIncremental, RegisterBufRingWithFlags, RegisteredBuffer, RegisteredBufferCount
  • Budget-based configuration: OptionsForBudget, BufferConfigForBudget, OptionsForSystem
  • Per-operation customization through OpOptionFunc
  • Timeout, cancel, poll, epoll, futex, waitid, msg-ring, splice, tee, pipe operations
  • Multishot subscription with MultishotSubscription, MultishotHandler, MultishotStep, and MultishotSubscriber
  • Listener setup: ListenerManager, ListenerOp, DecodeListenerCQE, PrepareListenerBind, PrepareListenerListen, SetListenerReady

Compatibility

  • Go: 1.26+
  • Platforms: Linux 6.18+