Mercurial > hg > nginx
view src/os/unix/ngx_readv_chain.c @ 8782:b3f6ad181df4 quic
QUIC: refactored CRYPTO and STREAM buffer ordering.
Generic function ngx_quic_order_bufs() is introduced. This function creates
and maintains a chain of buffers with holes. Holes are marked with b->sync
flag. Several buffers and holes in this chain may share the same underlying
memory buffer.
When processing STREAM frames with this function, frame data is copied only
once to the right place in the stream input chain. Previously data could
be copied twice. First when buffering an out-of-order frame data, and then
when filling stream buffer from ordered frame queue. Now there's only one
data chain for both tasks.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Tue, 25 May 2021 13:55:12 +0300 |
parents | efd71d49bde0 |
children | 7f5e3595caff |
line wrap: on
line source
/* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_event.h> ssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit) { u_char *prev; ssize_t n, size; ngx_err_t err; ngx_array_t vec; ngx_event_t *rev; struct iovec *iov, iovs[NGX_IOVS_PREALLOCATE]; rev = c->read; #if (NGX_HAVE_KQUEUE) if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, "readv: eof:%d, avail:%d, err:%d", rev->pending_eof, rev->available, rev->kq_errno); if (rev->available == 0) { if (rev->pending_eof) { rev->ready = 0; rev->eof = 1; ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno, "kevent() reported about an closed connection"); if (rev->kq_errno) { rev->error = 1; ngx_set_socket_errno(rev->kq_errno); return NGX_ERROR; } return 0; } else { return NGX_AGAIN; } } } #endif #if (NGX_HAVE_EPOLLRDHUP) if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "readv: eof:%d, avail:%d", rev->pending_eof, rev->available); if (rev->available == 0 && !rev->pending_eof) { return NGX_AGAIN; } } #endif prev = NULL; iov = NULL; size = 0; vec.elts = iovs; vec.nelts = 0; vec.size = sizeof(struct iovec); vec.nalloc = NGX_IOVS_PREALLOCATE; vec.pool = c->pool; /* coalesce the neighbouring bufs */ while (chain) { n = chain->buf->end - chain->buf->last; if (limit) { if (size >= limit) { break; } if (size + n > limit) { n = (ssize_t) (limit - size); } } if (prev == chain->buf->last) { iov->iov_len += n; } else { if (vec.nelts >= IOV_MAX) { break; } iov = ngx_array_push(&vec); if (iov == NULL) { return NGX_ERROR; } iov->iov_base = (void *) chain->buf->last; iov->iov_len = n; } size += n; prev = chain->buf->end; chain = chain->next; } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "readv: %ui, last:%uz", vec.nelts, iov->iov_len); do { n = readv(c->fd, (struct iovec *) vec.elts, vec.nelts); if (n == 0) { rev->ready = 0; rev->eof = 1; #if (NGX_HAVE_KQUEUE) /* * on FreeBSD readv() may return 0 on closed socket * even if kqueue reported about available data */ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { rev->available = 0; } #endif return 0; } if (n > 0) { #if (NGX_HAVE_KQUEUE) if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { rev->available -= n; /* * rev->available may be negative here because some additional * bytes may be received between kevent() and readv() */ if (rev->available <= 0) { if (!rev->pending_eof) { rev->ready = 0; } rev->available = 0; } return n; } #endif #if (NGX_HAVE_FIONREAD) if (rev->available >= 0) { rev->available -= n; /* * negative rev->available means some additional bytes * were received between kernel notification and readv(), * and therefore ev->ready can be safely reset even for * edge-triggered event methods */ if (rev->available < 0) { rev->available = 0; rev->ready = 0; } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "readv: avail:%d", rev->available); } else if (n == size) { if (ngx_socket_nread(c->fd, &rev->available) == -1) { n = ngx_connection_error(c, ngx_socket_errno, ngx_socket_nread_n " failed"); break; } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "readv: avail:%d", rev->available); } #endif #if (NGX_HAVE_EPOLLRDHUP) if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ngx_use_epoll_rdhup) { if (n < size) { if (!rev->pending_eof) { rev->ready = 0; } rev->available = 0; } return n; } #endif if (n < size && !(ngx_event_flags & NGX_USE_GREEDY_EVENT)) { rev->ready = 0; } return n; } err = ngx_socket_errno; if (err == NGX_EAGAIN || err == NGX_EINTR) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, "readv() not ready"); n = NGX_AGAIN; } else { n = ngx_connection_error(c, err, "readv() failed"); break; } } while (err == NGX_EINTR); rev->ready = 0; if (n == NGX_ERROR) { c->read->error = 1; } return n; }