/* * * Copyright 2026 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */
// Package readyreader provides utilities to perform non-memory-pinning reads.
package readyreaderimport ()// Reader is an optional interface that can be implemented by [net.Conn]// implementations to enable gRPC to perform non-memory-pinning reads.typeReaderinterface {// ReadOnReady waits for data to arrive, fetches a buffer, and performs a // read. When the underlying IO is readable, it allocates a buffer of size // bufSize from the pool and reads up to bufSize bytes into the buffer. // // It returns a pointer to the buffer so it can be returned to the pool // later, the number of bytes read, and an error. // // Callers should always process the n > 0 bytes returned before considering // the error. Doing so correctly handles I/O errors that happen after // reading some bytes, as well as both of the allowed EOF behaviors.ReadOnReady(bufSize int, pool mem.BufferPool) (b *[]byte, n int, err error)}// nonBlockingReader is optimized for non-memory-pinning reads using the RawConn// interface.typenonBlockingReaderstruct {rawsyscall.RawConn// The following fields are stored as field to avoid heap allocations.statereadStatedoReadfunc(fd uintptr) bool}typereadStatestruct {// Request params.bufSizeintpoolmem.BufferPool// Response params.readErrorerrorbytesReadintbuf *[]byte}// NewNonBlocking returns a ReadyReader if the passed reader supports// non-memory-pinning reads, else nil.func ( io.Reader) Reader {if , := .(Reader); {return }if !isRawConnSupported() {returnnil }// We restrict the types before asserting syscall.Conn. The credentials // package may return a wrapper that implements syscall.Conn by embedding // both the raw connection and the encrypted connection. If the code // attempts to read directly from the raw syscall.RawConn, it would read // encrypted data.switch .(type) {case *net.TCPConn, *net.UDPConn, *net.UnixConn, *net.IPConn:default:returnnil } , := .(syscall.Conn)if ! {returnnil } , := .SyscallConn()if != nil {returnnil } := &nonBlockingReader{raw: } .doRead = func( uintptr) bool { := &.state .buf = .pool.Get(.bufSize) .bytesRead, .readError = sysRead(, *.buf)if .readError != nil { .pool.Put(.buf) .buf = nil }return !wouldBlock(.readError) }return}func ( *nonBlockingReader) ( int, mem.BufferPool) (*[]byte, int, error) { .state = readState{pool: ,bufSize: , } := .raw.Read(.doRead) := .state.buf := .state.bytesRead := .state.readError .state = readState{}if != nil {if != nil { .Put() }returnnil, 0, }if != nil {// buffer is already released in the callback.returnnil, 0, }if == 0 {// syscall.Read doesn't consider a graceful socket closure to be an // error condition, but Go's io.Reader expects an EOF error. .Put()returnnil, 0, io.EOF }return , , nil}typeblockingReaderstruct {readerio.Reader}func ( *blockingReader) ( int, mem.BufferPool) (*[]byte, int, error) { := .Get() , := .reader.Read(*)if != nil { .Put()returnnil, 0, }return , , nil}// New detects if [syscall.RawConn] is available for non-memory-pinning reads.// If [syscall.RawConn] is unavailable, it falls back to using the simpler// [io.Reader] interface for reads.func ( io.Reader) Reader {if := NewNonBlocking(); != nil {return }return &blockingReader{reader: }}// bufReadyReader implements buffering for a ReadyReader object.// A new bufReadyReader is created by calling [NewBuffered].typebufReadyReaderstruct {buf *[]bytepoolmem.BufferPoolbufSizeintrdReader// reader provided by the callerr, wint// buf read and write positionserrerrorconstPoolconstBufferPool// stored as a field to avoid heap allocations.}// NewBuffered returns a new [io.Reader] with a buffer of the specified size// which is allocated from the provided pool.func ( Reader, int, mem.BufferPool) io.Reader {return &bufReadyReader{rd: ,pool: ,bufSize: , }}func ( *bufReadyReader) () error { := .err .err = nilreturn}func ( *bufReadyReader) () int { return .w - .r }// Read reads data into p. It returns the number of bytes read into p. The// bytes are taken from at most one Read on the underlying [ReadyReader],// hence n may be less than len(p). If the underlying [ReadyReader] can return// a non-zero count with io.EOF, then this Read method can do so as well; see// the [io.Reader] docs.func ( *bufReadyReader) ( []byte) ( int, error) { = len()if == 0 {if .buffered() > 0 {return0, nil }return0, .readErr() }if .r == .w {if .err != nil {return0, .readErr() }iflen() >= .bufSize {// Large read, empty buffer. // Read directly into p to avoid copy. .constPool.buffer = _, , .err = .rd.ReadOnReady(len(), &.constPool)return , .readErr() }// One read. .r = 0 .w = 0 .buf, , .err = .rd.ReadOnReady(.bufSize, .pool)if == 0 {if .buf != nil { .pool.Put(.buf) .buf = nil }return0, .readErr() } .w += }// copy as much as we can // b.buf must be non-nil since b.r != b.w. := *.buf = copy(, [.r:.w]) .r += if .r == .w {// Consumed entire buffer, release it. .pool.Put(.buf) .buf = nil }return , nil}typeconstBufferPoolstruct {buffer []byte}func ( *constBufferPool) (int) *[]byte {return &.buffer}func ( *constBufferPool) (*[]byte) {}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)