diff src/os/unix/ngx_linux_sendfile_chain.c @ 195:8dee38ea9117

nginx-0.0.1-2003-11-25-23:44:56 import
author Igor Sysoev <igor@sysoev.ru>
date Tue, 25 Nov 2003 20:44:56 +0000
parents 70d2345a903f
children 11fbd0fc041d
line wrap: on
line diff
--- a/src/os/unix/ngx_linux_sendfile_chain.c
+++ b/src/os/unix/ngx_linux_sendfile_chain.c
@@ -1,183 +1,214 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_linux_init.h>
+#include <ngx_event.h>
 
 
 ngx_chain_t *ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in)
 {
-    int              rc, on, off;
+    int              rc;
     char            *prev;
-    size_t           hsize, size;
-    ssize_t          sent;
+    off_t            fprev;
+    size_t           size, fsize, sent;
+    ngx_int_t        use_cork, eintr;
     struct iovec    *iov;
-    struct sf_hdtr   hdtr;
     ngx_err_t        err;
-    ngx_array_t      header, trailer;
     ngx_hunk_t      *file;
-    ngx_chain_t     *ce;
+    ngx_array_t      header;
+    ngx_event_t     *wev;
+    ngx_chain_t     *cl;
+
+    wev = c->write;
 
-    ce = in;
-    file = NULL;
-    hsize = 0;
+    if (!wev->ready) {
+        return in;
+    }
+
+    cork = 0;
 
-    on = 1;
-    off = 0;
+    do {
+        file = NULL;
+        fsize = 0;
+        eintr = 0;
 
-    ngx_init_array(header, c->pool, 10, sizeof(struct iovec), NGX_CHAIN_ERROR);
-    ngx_init_array(trailer, c->pool, 10, sizeof(struct iovec), NGX_CHAIN_ERROR);
+        ngx_init_array(header, c->pool, 10, sizeof(struct iovec),
+                       NGX_CHAIN_ERROR);
 
-    /* create the header iovec */
-    if (ngx_hunk_in_memory_only(ce->hunk)) {
         prev = NULL;
         iov = NULL;
 
-        /* create the iovec and coalesce the neighbouring chain entries */
-        while (ce && ngx_hunk_in_memory_only(ce->hunk)) {
+        /* create the iovec and coalesce the neighbouring hunks */
+
+        for (cl = in; cl; cl = cl->next) {
+            if (ngx_hunk_special(cl->hunk)) {
+                continue;
+            }
 
-            if (prev == ce->hunk->pos) {
-                iov->iov_len += ce->hunk->last - ce->hunk->pos;
-                prev = ce->hunk->last;
+            if (!ngx_hunk_in_memory_only(cl->hunk)) {
+                break;
+            }
+
+            if (prev == cl->hunk->pos) {
+                iov->iov_len += cl->hunk->last - cl->hunk->pos;
 
             } else {
                 ngx_test_null(iov, ngx_push_array(&header), NGX_CHAIN_ERROR);
-                iov->iov_base = ce->hunk->pos;
-                iov->iov_len = ce->hunk->last - ce->hunk->pos;
-                prev = ce->hunk->last;
-            }
-
-            if (ngx_freebsd_sendfile_nbytes_bug) {
-                hsize += ce->hunk->last - ce->hunk->pos;
+                iov->iov_base = cl->hunk->pos;
+                iov->iov_len = cl->hunk->last - cl->hunk->pos;
             }
 
-            ce = ce->next;
-        }
-    }
-
-    /* TODO: coalesce the neighbouring file hunks */
-    if (ce && (ce->hunk->type & NGX_HUNK_FILE)) {
-        file = ce->hunk;
-        ce = ce->next;
-    }
-
-    /* create the trailer iovec */
-    if (ce && ngx_hunk_in_memory_only(ce->hunk)) {
-        prev = NULL;
-        iov = NULL;
-
-        /* create the iovec and coalesce the neighbouring chain entries */
-        while (ce && ngx_hunk_in_memory_only(ce->hunk)) {
-
-            if (prev == ce->hunk->pos) {
-                iov->iov_len += ce->hunk->last - ce->hunk->pos;
-                prev = ce->hunk->last;
-
-            } else {
-                ngx_test_null(iov, ngx_push_array(&trailer), NGX_CHAIN_ERROR);
-                iov->iov_base = ce->hunk->pos;
-                iov->iov_len = ce->hunk->last - ce->hunk->pos;
-                prev = ce->hunk->last;
-            }
-
-            ce = ce->next;
-        }
-    }
-
-    if (file) {
-        if (setsockopt(c->fd, IPPROTO_TCP, TCP_CORK,
-                       (const void *) &on, sizeof(int)) == -1) {
-            ngx_log_error(NGX_LOG_CRIT, c->log, err,
-                          "setsockopt(TCP_CORK, 1) failed");
-            return NGX_CHAIN_ERROR;
+            prev = cl->hunk->last;
         }
 
-
-        rc = sendfile(c->fd, file->file->fd, file->file_pos,
-                        (size_t) (file->file_last - file->file_pos));
+        /* set TCP_CORK if there is a header before a file */
 
-        if (rc == -1) {
-            err = ngx_errno;
-            if (err == NGX_EAGAIN) {
-                ngx_log_error(NGX_LOG_INFO, c->log, err, "senfile() EAGAIN");
+        if (!c->tcp_nopush
+            && header.nelts != 0
+            && cl
+            && cl->hunk->type & NGX_HUNK_FILE)
+        {
+            c->tcp_nopush = 1;
 
-            } else if (err == NGX_EINTR) {
-                ngx_log_error(NGX_LOG_INFO, c->log, err, "senfile() EINTR");
+ngx_log_debug(c->log, "CORK");
 
-            } else {
-                ngx_log_error(NGX_LOG_CRIT, c->log, err, "sendfile() failed");
+            if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
+                ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno,
+                              ngx_tcp_nopush_n " failed");
                 return NGX_CHAIN_ERROR;
             }
         }
 
-        sent = rc > 0 ? rc : 0;
+        if (header.nelts == 0 && cl && cl->hunk->type & NGX_HUNK_FILE) {
+
+            /* get the file hunk */
 
-#if (NGX_DEBUG_WRITE_CHAIN)
-        ngx_log_debug(c->log, "sendfile: %d, @%qd %d:%d" _
-                      rc _ file->file_pos _ sent _
-                      (size_t) (file->file_last - file->file_pos));
-#endif
+            file = cl->hunk;
+            fsize = (size_t) (file->file_last - file->file_pos);
+            fprev = file->file_last;
+            cl = cl->next; 
 
-    } else {
-        rc = writev(c->fd, (struct iovec *) header.elts, header.nelts);
+            /* coalesce the neighbouring file hunks */
 
-        if (rc == -1) {
-            err = ngx_errno;
-            if (err == NGX_EAGAIN) {
-                ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EAGAIN");
+            while (cl && (cl->hunk->type & NGX_HUNK_FILE)) {
+                if (file->file->fd != cl->hunk->file->fd
+                    || fprev != cl->hunk->file_pos)
+                {
+                    break;
+                }
 
-            } else if (err == NGX_EINTR) {
-                ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EINTR");
-
-            } else {
-                ngx_log_error(NGX_LOG_CRIT, c->log, err, "writev() failed");
-                return NGX_CHAIN_ERROR;
+                fsize += (size_t) (cl->hunk->file_last - cl->hunk->file_pos);
+                fprev = cl->hunk->file_last;
+                cl = cl->next;
             }
         }
 
-        sent = rc > 0 ? rc : 0;
+        /* 
+         * the tail is the rest of the chain that exceeded
+         * a single sendfile() capability
+         */
+
+        tail = cl;
+
+        if (fsize) {
+            rc = sendfile(c->fd, file->file->fd, file->file_pos, fsize);
+
+            if (rc == -1) {
+                err = ngx_errno;
+                if (err == NGX_EAGAIN) {
+                    ngx_log_error(NGX_LOG_INFO, c->log, err,
+                                  "sendfile() EAGAIN");
+
+                } else if (err == NGX_EINTR) {
+                    eintr = 1;
+                    ngx_log_error(NGX_LOG_INFO, c->log, err,
+                                  "sendfile() EINTR");
+
+                } else {
+                    ngx_log_error(NGX_LOG_CRIT, c->log, err,
+                                  "sendfile() failed");
+                    return NGX_CHAIN_ERROR;
+                }
+            }
+
+            sent = rc > 0 ? rc : 0;
 
 #if (NGX_DEBUG_WRITE_CHAIN)
-        ngx_log_debug(c->log, "writev: %d" _ sent);
+            ngx_log_debug(c->log, "sendfile: %d, @" OFF_T_FMT " %d:%d" _
+                          rc _ file->file_pos _ sent _ fsize);
 #endif
-    }
-
-    c->sent += sent;
-
-    for (ce = in; ce && sent > 0; ce = ce->next) {
+        } else {
+            rc = writev(c->fd, header.elts, header.nelts);
 
-        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
-            size = ce->hunk->last - ce->hunk->pos;
-        } else {
-            size = ce->hunk->file_last - ce->hunk->file_pos;
-        }
+            if (rc == -1) {
+                err = ngx_errno;
+                if (err == NGX_EAGAIN) {
+                    ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EAGAIN");
 
-        if (sent >= size) {
-            sent -= size;
+                } else if (err == NGX_EINTR) {
+                    eintr = 1;
+                    ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EINTR");
 
-            if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
-                ce->hunk->pos = ce->hunk->last;
+                } else {
+                    ngx_log_error(NGX_LOG_CRIT, c->log, err, "writev() failed");
+                    return NGX_CHAIN_ERROR;
+                }
             }
 
-            if (ce->hunk->type & NGX_HUNK_FILE) {
-                ce->hunk->file_pos = ce->hunk->file_last;
-            }
+            sent = rc > 0 ? rc : 0;
 
-            continue;
-        }
-
-        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
-            ce->hunk->pos += sent;
+#if (NGX_DEBUG_WRITE_CHAIN)
+            ngx_log_debug(c->log, "writev: %d" _ sent);
+#endif
         }
 
-        if (ce->hunk->type & NGX_HUNK_FILE) {
-            ce->hunk->file_pos += sent;
+        c->sent += sent;
+
+        for (cl = in; cl; cl = cl->next) {
+
+            if (ngx_hunk_special(cl->hunk)) {
+                continue;
+            }
+
+            if (sent == 0) {
+                break;
+            }
+
+            size = ngx_hunk_size(cl->hunk);
+
+            if (sent >= size) {
+                sent -= size;
+
+                if (cl->hunk->type & NGX_HUNK_IN_MEMORY) {
+                    cl->hunk->pos = cl->hunk->last;
+                }
+
+                if (cl->hunk->type & NGX_HUNK_FILE) {
+                    cl->hunk->file_pos = cl->hunk->file_last;
+                }
+
+                continue;
+            }
+
+            if (cl->hunk->type & NGX_HUNK_IN_MEMORY) {
+                cl->hunk->pos += sent;
+            }
+
+            if (cl->hunk->type & NGX_HUNK_FILE) {
+                cl->hunk->file_pos += sent;
+            }
+
+            break;
         }
 
-        break;
+        in = cl;
+
+        /* "tail == in" means that a single sendfile() is complete */
+
+    } while ((tail && tail == in) || eintr);
+
+    if (in) {
+        wev->ready = 0;
     }
 
-    ngx_destroy_array(&trailer);
-    ngx_destroy_array(&header);
-
-    return ce;
+    return in;
 }