Mercurial > hg > nginx-quic
view src/os/win32/ngx_wsasend_chain.c @ 5170:6362bd26e4b0
Events: protection from stale events in eventport and devpoll.
Stale write event may happen if read and write events was reported both,
and processing of the read event closed descriptor.
In practice this might result in "sendfilev() failed (134: ..." or
"writev() failed (134: ..." errors when switching to next upstream server.
See report here:
http://mailman.nginx.org/pipermail/nginx/2013-April/038421.html
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Fri, 12 Apr 2013 15:02:33 +0000 |
parents | d620f497c50f |
children | f9c83484d9ce |
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> #define NGX_WSABUFS 8 ngx_chain_t * ngx_wsasend_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) { int rc; u_char *prev; u_long size, sent, send, prev_send; ngx_uint_t complete; ngx_err_t err; ngx_event_t *wev; ngx_array_t vec; ngx_chain_t *cl; LPWSABUF wsabuf; WSABUF wsabufs[NGX_WSABUFS]; wev = c->write; if (!wev->ready) { return in; } /* the maximum limit size is the maximum u_long value - the page size */ if (limit == 0 || limit > NGX_MAX_UINT32_VALUE - ngx_pagesize) { limit = NGX_MAX_UINT32_VALUE - ngx_pagesize; } send = 0; complete = 0; /* * WSABUFs must be 4-byte aligned otherwise * WSASend() will return undocumented WSAEINVAL error. */ vec.elts = wsabufs; vec.size = sizeof(WSABUF); vec.nalloc = NGX_WSABUFS; vec.pool = c->pool; for ( ;; ) { prev = NULL; wsabuf = NULL; prev_send = send; vec.nelts = 0; /* create the WSABUF and coalesce the neighbouring bufs */ for (cl = in; cl && vec.nelts < ngx_max_wsabufs && send < limit; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } size = cl->buf->last - cl->buf->pos; if (send + size > limit) { size = (u_long) (limit - send); } if (prev == cl->buf->pos) { wsabuf->len += cl->buf->last - cl->buf->pos; } else { wsabuf = ngx_array_push(&vec); if (wsabuf == NULL) { return NGX_CHAIN_ERROR; } wsabuf->buf = (char *) cl->buf->pos; wsabuf->len = cl->buf->last - cl->buf->pos; } prev = cl->buf->last; send += size; } sent = 0; rc = WSASend(c->fd, vec.elts, vec.nelts, &sent, 0, NULL, NULL); if (rc == -1) { err = ngx_errno; if (err == WSAEWOULDBLOCK) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, "WSASend() not ready"); } else { wev->error = 1; ngx_connection_error(c, err, "WSASend() failed"); return NGX_CHAIN_ERROR; } } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "WSASend: fd:%d, s:%ul", c->fd, sent); if (send - prev_send == sent) { complete = 1; } c->sent += sent; for (cl = in; cl; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } if (sent == 0) { break; } size = cl->buf->last - cl->buf->pos; if (sent >= size) { sent -= size; cl->buf->pos = cl->buf->last; continue; } cl->buf->pos += sent; break; } if (!complete) { wev->ready = 0; return cl; } if (send >= limit || cl == NULL) { return cl; } in = cl; } } ngx_chain_t * ngx_overlapped_wsasend_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) { int rc; u_char *prev; u_long size, send, sent; ngx_err_t err; ngx_event_t *wev; ngx_array_t vec; ngx_chain_t *cl; LPWSAOVERLAPPED ovlp; LPWSABUF wsabuf; WSABUF wsabufs[NGX_WSABUFS]; wev = c->write; if (!wev->ready) { return in; } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "wev->complete: %d", wev->complete); if (!wev->complete) { /* post the overlapped WSASend() */ /* the maximum limit size is the maximum u_long value - the page size */ if (limit == 0 || limit > NGX_MAX_UINT32_VALUE - ngx_pagesize) { limit = NGX_MAX_UINT32_VALUE - ngx_pagesize; } /* * WSABUFs must be 4-byte aligned otherwise * WSASend() will return undocumented WSAEINVAL error. */ vec.elts = wsabufs; vec.nelts = 0; vec.size = sizeof(WSABUF); vec.nalloc = NGX_WSABUFS; vec.pool = c->pool; send = 0; prev = NULL; wsabuf = NULL; /* create the WSABUF and coalesce the neighbouring bufs */ for (cl = in; cl && vec.nelts < ngx_max_wsabufs && send < limit; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } size = cl->buf->last - cl->buf->pos; if (send + size > limit) { size = (u_long) (limit - send); } if (prev == cl->buf->pos) { wsabuf->len += cl->buf->last - cl->buf->pos; } else { wsabuf = ngx_array_push(&vec); if (wsabuf == NULL) { return NGX_CHAIN_ERROR; } wsabuf->buf = (char *) cl->buf->pos; wsabuf->len = cl->buf->last - cl->buf->pos; } prev = cl->buf->last; send += size; } ovlp = (LPWSAOVERLAPPED) &c->write->ovlp; ngx_memzero(ovlp, sizeof(WSAOVERLAPPED)); rc = WSASend(c->fd, vec.elts, vec.nelts, &sent, 0, ovlp, NULL); wev->complete = 0; if (rc == -1) { err = ngx_errno; if (err == WSA_IO_PENDING) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, "WSASend() posted"); wev->active = 1; return in; } else { wev->error = 1; ngx_connection_error(c, err, "WSASend() failed"); return NGX_CHAIN_ERROR; } } else if (ngx_event_flags & NGX_USE_IOCP_EVENT) { /* * if a socket was bound with I/O completion port then * GetQueuedCompletionStatus() would anyway return its status * despite that WSASend() was already complete */ wev->active = 1; return in; } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "WSASend: fd:%d, s:%ul", c->fd, sent); } else { /* the overlapped WSASend() complete */ wev->complete = 0; wev->active = 0; if (ngx_event_flags & NGX_USE_IOCP_EVENT) { if (wev->ovlp.error) { ngx_connection_error(c, wev->ovlp.error, "WSASend() failed"); return NGX_CHAIN_ERROR; } sent = wev->available; } else { if (WSAGetOverlappedResult(c->fd, (LPWSAOVERLAPPED) &wev->ovlp, &sent, 0, NULL) == 0) { ngx_connection_error(c, ngx_socket_errno, "WSASend() or WSAGetOverlappedResult() failed"); return NGX_CHAIN_ERROR; } } } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "WSASend ovlp: fd:%d, s:%ul", c->fd, sent); c->sent += sent; for (cl = in; cl && sent > 0; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } if (sent == 0) { break; } size = cl->buf->last - cl->buf->pos; if (sent >= size) { sent -= size; cl->buf->pos = cl->buf->last; continue; } cl->buf->pos += sent; break; } if (cl) { wev->ready = 0; } else { wev->ready = 1; } return cl; }