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));