Documentation
¶
Overview ¶
Package loop is the network domain's event-loop engine (ADR 0186): the native backends a plaintext stream group runs on when it is served by a corenet.LoopHandler — epoll on every Linux architecture, io_uring after a staged probe of the running kernel — and nothing else of the SDK.
It is a leaf below server (§D3): it imports internal/core/net, the shared UnsupportedPlatform of internal/core/proc and the kernel, never server. server binds or adopts the listeners, keeps the lifecycle, the admission and the drain, and hands the loop duplicated descriptors through one type, Listener. The portable backend stays in server: it needs the netpoller.
A loop is one goroutine locked to its OS thread, owning its rings or its epoll instance. Its connection table is pointer-free Go memory the race detector sees; every byte the kernel reads or writes — the read arena, the spill and output slab — is mapped outside the Go heap (§D7). The engine knows bytes, references, deadlines, write pressure and the order of writes; it speaks no HTTP.
An idle connection on a native backend costs at most 256 bytes of Go heap — its slot and its timer link — and no mapped byte: its output chunk is attached at its first Write and returned once drained.
Index ¶
- func CheckCapabilities(group string, v corenet.LoopValue) error
- func ResolveLoops(n int) (int, string)
- func Unprobed(backend string, disabled []corenet.Capability, disabledBy string) []corenet.CapabilityValue
- type Config
- type Group
- type Listener
- type PacketConfig
- type PacketCountersValue
- type PacketLane
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CheckCapabilities ¶ added in v0.26.28
CheckCapabilities refuses a LoopValue.Capabilities entry the group cannot opt into: an engine, a class B capability or an unknown token, with LoopCapabilityRefused naming it (ADR 0189). A class A-kernel entry needs no opt-in and is accepted as it is. server calls it for the portable backend, which never reaches Start.
func ResolveLoops ¶ added in v0.26.36
ResolveLoops resolves LoopValue.Loops n and says where the count came from, as LoopStateValue.LoopsSource spells it: LoopsAuto is GOMAXPROCS less one read now, at least one, "auto" — the P left over runs the rest of the process (ADR 0199); a positive count is its own, "explicit"; anything else is one, "single" (R8). server calls it once per group, at Start, and keeps the snapshot for every backend.
func Unprobed ¶ added in v0.26.28
func Unprobed(backend string, disabled []corenet.Capability, disabledBy string) []corenet.CapabilityValue
Unprobed is the capability report of a group that runs on backend without the leaf's probes — the portable backend: every row unprobed, or disabled by what the operator switched off, as disabledBy says (ADR 0189).
Types ¶
type Config ¶
type Config struct {
// Group is the group's name, for the start line and State.
Group string
// Value is the group's LoopValue.
Value corenet.LoopValue
// MaxConns is LimitsValue.MaxConns: past it a connection is accepted and
// closed at once. Zero sets no ceiling.
MaxConns int
// IdleTimeout closes a connection with no traffic for that long, its
// output sent. Zero sets none.
IdleTimeout time.Duration
// ReadTimeout is the group's read bound. TCP_DEFER_ACCEPT, once opted into,
// takes it in whole seconds, rounded up. Zero sets none, and the option is
// not set.
ReadTimeout time.Duration
// KeepAlive is the idle time before the first keepalive probe of an
// accepted socket, and the interval between probes: zero is 15 s, the
// standard library's, and negative sets no keepalive.
KeepAlive time.Duration
// UserTimeout is TCP_USER_TIMEOUT, the time sent output may stay
// unacknowledged before the kernel closes the connection with an error,
// rounded up to whole milliseconds. Zero sets none.
UserTimeout time.Duration
// Handler is the group's loop handler.
Handler corenet.LoopHandler
// Busy reports whether a connection of a lane waits on its worker (the
// chain's LoopOffloader.Pending, R3): the drain keeps it open, eviction
// passes it by. Nil when nothing offloads.
Busy func(lane int, ref corenet.LoopConnRef) bool
// Listeners are the sockets to accept on.
Listeners []Listener
// Log receives the one start line; nil is standard error.
Log io.Writer
// Disabled are the capabilities the operator switched off: never probed,
// never used, reported "disabled" (ADR 0189).
Disabled []corenet.Capability
// DisabledBy says how they were switched off in State; empty is
// KITSUNIUM_NET_DISABLE.
DisabledBy string
// LoopsSource is where Value.Loops came from, for State, when server
// resolved it already: LoopsAuto read as GOMAXPROCS is "auto". Empty lets
// the group say it itself.
LoopsSource string
// Memory is the soft-memory-limit derivation server read (memlimit.Derive):
// its Allowance, when one was found, sizes a slab budget LoopValue leaves
// at zero (#809). The zero value derives nothing.
Memory coreproc.MemoryLimitValue
// contains filtered or unexported fields
}
Config is what server gives a loop group to start: the group's name, its LoopValue, the options the engine honours itself — MaxConns, IdleTimeout and the socket options of ADR 0189's rows: ReadTimeout, KeepAlive, UserTimeout —, its handler and its listeners.
type Group ¶
type Group struct {
// contains filtered or unexported fields
}
Group is a running loop group: its loops, their threads and their rings. Start is the one way to get one, so a Group always runs, or ran and was stopped by Shutdown or Close.
func Start ¶
Start resolves cfg against the effective RLIMIT_NOFILE — refusing with LoopNoDescriptors when it is zero or unreadable, before the one ring formula divides by it — starts the group's loops on the backend cfg asks for, and writes one line to cfg.Log naming the backend, the probe stage, any fallback reason, the loops, the rings and their sizes. The listeners are the group's from the call on, started or refused. An explicit backend the platform has no mechanic for is UnsupportedPlatform; one the kernel's probe refused is LoopBackendUnavailable.
func (*Group) Shutdown ¶
Shutdown drains the group, until ctx is done; then it closes the rest. The drain stops accepting without resetting a connection the kernel already completed: every loop cancels its pending accepts — serving one that completed as the cancel crossed it —, takes the listen queue until it is empty, until 65536 connections came from one listener — arrivals outpacing the drain —, or until descriptors run out with no connection of the group left whose close would free one, the rest of the queue reset by the close then; the last loop to take closes the listener. Then each connection closes once it settled, its output sent, a connection that has sent nothing yet once it has or stayed silent for a second. A drain cut short returns DrainTimeout.
func (*Group) State ¶
func (g *Group) State() corenet.LoopStateValue
State reports the group's backend and every ring's counters.
type Listener ¶
type Listener struct {
// FD is the duplicated descriptor, close-on-exec.
FD int
// Loop is one more than the index of the only loop that accepts on it — a
// SO_REUSEPORT shard (#856) —, zero when every loop of the group does.
Loop int
}
Listener is the one hand-off from server to the loop: a listening socket server has already bound or adopted, as a duplicated descriptor the loop group owns from then on and closes when it stops.
type PacketConfig ¶ added in v0.30.0
type PacketConfig struct {
// Group is the group's name.
Group string
// FD is the UDP socket's duplicated descriptor, close-on-exec.
FD int
// Lane is the lane's index, PacketLoopIO.Loop's answer.
Lane int
// Ceiling is the group's MaxPacketSize: a datagram past it is counted in
// Counters.Oversized and never handed on.
Ceiling int
// Handler is the group's datagram loop handler.
Handler corenet.PacketLoopHandler
// Counters are where the lane counts what State reports.
Counters PacketCountersValue
// OnGSOOff is called once, on the lane's thread, when the device refuses a
// UDP_SEGMENT send with EIO and the lane sends one datagram a message from
// then on. Nil calls nothing.
OnGSOOff func()
}
PacketConfig is what server gives one datagram lane to start (#852): the socket, as a duplicated non-blocking descriptor the lane owns from the call on, started or refused, the lane's index, the group's ceiling, its handler, and the counters State sums.
type PacketCountersValue ¶ added in v0.30.0
type PacketCountersValue struct {
// Total counts the datagrams received.
Total *atomic.Uint64
// Oversized counts the datagrams past the ceiling, dropped.
Oversized *atomic.Uint64
// Truncated counts the messages the kernel cut, dropped.
Truncated *atomic.Uint64
// ReplyFailures counts the queued replies the kernel refused.
ReplyFailures *atomic.Uint64
// GSOOversized counts the UDP_SEGMENT sends refused with EMSGSIZE.
GSOOversized *atomic.Uint64
// KernelDrops adds the growth of the socket's SO_RXQ_OVFL count.
KernelDrops *atomic.Uint64
}
PacketCountersValue are server's counters a datagram lane adds to, once a batch: every one must be set.
type PacketLane ¶ added in v0.30.0
type PacketLane interface {
// Handover drains the socket before its successor reads it (ADR 0192 §D4):
// the lane stops its receive, serves what the socket queued to EAGAIN,
// sends the replies, switches UDP GRO off and stops; Done is closed then.
Handover()
// Stop stops the lane now, its queued datagrams left in the socket, and
// waits until its thread has returned.
Stop()
// Done is closed once the lane's thread has returned.
Done() <-chan struct{}
}
PacketLane is one running datagram lane (#852): a thread locked to it, its own io_uring, the socket's multishot RECVMSG and its replies' SENDMSG. StartPacket is the one way to get one.
IFACE-OPAQUE: the lane is the leaf's; server drives its life cycle only.
func StartPacket ¶ added in v0.30.0
func StartPacket(cfg *PacketConfig) (PacketLane, error)
StartPacket starts one datagram lane on io_uring (#852): a thread locked to it probes the kernel — the ring, RECVMSG and SENDMSG in REGISTER_PROBE, the provided-buffer ring, then a loopback multishot RECVMSG self-test — and only then serves cfg.FD. A failed stage is LoopBackendUnavailable naming it, the descriptor closed, and server serves the socket on its batched reader; off the io_uring architectures and off Linux it is LoopBackendUnavailable at the stage "arch", before anything is tried.
Goroutine lifecycle: one per lane, locked to its OS thread, started here; it returns once Stop is asked or a handover drained the socket, and closes Done last.
IFACE-OPAQUE: the lane's state is the loop thread's alone; server drives its life cycle and nothing else.
Source Files
¶
- affinity_linux.go
- busypoll.go
- busypoll_epoll_linux.go
- busypoll_ioc_linux.go
- busypoll_uring_linux.go
- caps.go
- conn.go
- core.go
- core_file.go
- debug_off_linux.go
- doc.go
- drain_native.go
- drain_take_linux.go
- epoll_compliance.go
- epoll_linux.go
- fanwake.go
- gen_ports.go
- group.go
- group_linux.go
- listener_linux.go
- listener_native.go
- packet.go
- packet_pktinfo_linux.go
- packet_uring_linux.go
- peer_compliance.go
- peer_cred_linux.go
- peer_linux.go
- postq.go
- sizing.go
- slab.go
- stats.go
- uring_abi_linux.go
- uring_bundle_linux.go
- uring_caps_linux.go
- uring_compliance.go
- uring_fanwake_linux.go
- uring_loop_linux.go
- uring_peer_linux.go
- uring_probe_linux.go
- uring_ring_linux.go
- uring_sendzc_linux.go
- wheel.go
- zcrx.go
- zcrx_uring_linux.go