dgaudet 97/07/28 23:52:28
Modified: src CHANGES buff.c Log: Combine large bwrite() with partial buffer by using writev() to avoid any mem->mem copies. Fix a bug where if bflush/bwrite had a write failure while chunking they won't properly start the next chunk. Revision Changes Path 1.371 +10 -0 apache/src/CHANGES Index: CHANGES =================================================================== RCS file: /export/home/cvs/apache/src/CHANGES,v retrieving revision 1.370 retrieving revision 1.371 diff -u -r1.370 -r1.371 --- CHANGES 1997/07/28 18:22:40 1.370 +++ CHANGES 1997/07/29 06:52:25 1.371 @@ -1,5 +1,15 @@ Changes with Apache 1.3a2 + *) When a large bwrite() occurs (larger than the internal buffer size), + while there is already something in the buffer, apache will combine + the large write and the buffer into a single writev(). (This is + in anticipation of using mmap() for reading files.) + [Dean Gaudet] + + *) In obscure cases where a partial socket write occured while chunking, + Apache would omit the chunk header/footer on the next block. + [Dean Gaudet] + *) API: Added child_exit function to module structure. This is called once per "heavy-weight process" just before a server child exit()'s e.g. when max_requests_per_child is reached, etc. 1.39 +103 -39 apache/src/buff.c Index: buff.c =================================================================== RCS file: /export/home/cvs/apache/src/buff.c,v retrieving revision 1.38 retrieving revision 1.39 diff -u -r1.38 -r1.39 --- buff.c 1997/07/24 04:23:57 1.38 +++ buff.c 1997/07/29 06:52:26 1.39 @@ -840,6 +840,45 @@ } +#ifndef NO_WRITEV +/* similar to previous, but uses writev. Note that it modifies vec. + * return 0 if successful, -1 otherwise. + */ +static int writev_it_all (BUFF *fb, struct iovec *vec, int nvec) +{ + int i, rv; + + /* while it's nice an easy to build the vector and crud, it's painful + * to deal with a partial writev() + */ + for( i = 0; i < nvec; ) { + do rv = writev( fb->fd, &vec[i], nvec - i ); + while (rv == -1 && errno == EINTR && !(fb->flags & B_EOUT)); + if (rv == -1) + return -1; + /* recalculate vec to deal with partial writes */ + while (rv > 0) { + if (rv < vec[i].iov_len) { + vec[i].iov_base = (char *)vec[i].iov_base + rv; + vec[i].iov_len -= rv; + rv = 0; + if (vec[i].iov_len == 0) { + ++i; + } + } else { + rv -= vec[i].iov_len; + ++i; + } + } + if (fb->flags & B_EOUT) + return -1; + } + /* if we got here, we wrote it all */ + return 0; +} +#endif + + /* * A hook to write() that deals with chunking. This is really a protocol- * level issue, but we deal with it here because it's simpler; this is @@ -852,7 +891,6 @@ char chunksize[16]; /* Big enough for practically anything */ #ifndef NO_WRITEV struct iovec vec[3]; - int i, rv; #endif if (fb->flags & (B_WRERR|B_EOUT)) @@ -874,9 +912,6 @@ return -1; return nbyte; #else - -#define NVEC (sizeof(vec)/sizeof(vec[0])) - vec[0].iov_base = chunksize; vec[0].iov_len = ap_snprintf(chunksize, sizeof(chunksize), "%x\015\012", nbyte); @@ -884,38 +919,51 @@ vec[1].iov_len = nbyte; vec[2].iov_base = "\r\n"; vec[2].iov_len = 2; - /* while it's nice an easy to build the vector and crud, it's painful - * to deal with a partial writev() - */ - for( i = 0; i < NVEC; ) { - do rv = writev( fb->fd, &vec[i], NVEC - i ); - while (rv == -1 && errno == EINTR && !(fb->flags & B_EOUT)); - if (rv == -1) - return -1; - /* recalculate vec to deal with partial writes */ - while (rv > 0) { - if( rv <= vec[i].iov_len ) { - vec[i].iov_base = (char *)vec[i].iov_base + rv; - vec[i].iov_len -= rv; - rv = 0; - if( vec[i].iov_len == 0 ) { - ++i; - } - } else { - rv -= vec[i].iov_len; - ++i; - } - } - if (fb->flags & B_EOUT) - return -1; - } - /* if we got here, we wrote it all */ - return nbyte; -#undef NVEC + + return writev_it_all (fb, vec, (sizeof(vec)/sizeof(vec[0]))) ? -1 : nbyte; #endif } +#ifndef NO_WRITEV +/* + * Used to combine the contents of the fb buffer, and a large buffer + * passed in. + */ +static int large_write (BUFF *fb, const void *buf, int nbyte) +{ + struct iovec vec[4]; + int nvec; + char chunksize[16]; + + nvec = 0; + /* it's easiest to end the current chunk */ + if (fb->flags & B_CHUNK) { + end_chunk(fb); + } + vec[0].iov_base = fb->outbase; + vec[0].iov_len = fb->outcnt; + if (fb->flags & B_CHUNK) { + vec[1].iov_base = chunksize; + vec[1].iov_len = ap_snprintf (chunksize, sizeof(chunksize), + "%x\015\012", nbyte); + vec[2].iov_base = (void *)buf; + vec[2].iov_len = nbyte; + vec[3].iov_base = "\r\n"; + vec[3].iov_len = 2; + nvec = 4; + } else { + vec[1].iov_base = (void *)buf; + vec[1].iov_len = nbyte; + nvec = 2; + } + + fb->outcnt = 0; + return writev_it_all (fb, vec, nvec) ? -1 : nbyte; +} +#endif + + /* * Write nbyte bytes. * Only returns fewer than nbyte if an error ocurred. @@ -952,6 +1000,19 @@ return i; } +#ifndef NO_WRITEV +/* + * Detect case where we're asked to write a large buffer, and combine our + * current buffer with it in a single writev() + */ + if (fb->outcnt > 0 && nbyte >= fb->bufsiz) { + return large_write (fb, buf, nbyte); + } +#endif + + /* in case a chunk hasn't been started yet */ + if( fb->flags & B_CHUNK ) start_chunk( fb ); + /* * Whilst there is data in the buffer, keep on adding to it and writing it * out @@ -978,13 +1039,17 @@ /* it is just too painful to try to re-cram the buffer while * chunking */ - i = (write_it_all(fb, fb->outbase, fb->outcnt) == -1) ? - -1 : fb->outcnt; - } else { - do { - i = buff_write(fb, fb->outbase, fb->outcnt); - } while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT)); + if (write_it_all(fb, fb->outbase, fb->outcnt) == -1) { + /* we cannot continue after a chunked error */ + doerror (fb, B_WR); + return -1; + } + fb->outcnt = 0; + break; } + do { + i = buff_write(fb, fb->outbase, fb->outcnt); + } while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT)); if (i <= 0) { if (i == 0) /* return of 0 means non-blocking */ errno = EAGAIN; @@ -1062,7 +1127,6 @@ while (fb->outcnt > 0) { - /* the buffer must be full */ do { i = buff_write(fb, fb->outbase, fb->outcnt); } while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT));