Index: file_io/unix/readwrite.c =================================================================== --- file_io/unix/readwrite.c (revision 930006) +++ file_io/unix/readwrite.c (working copy) @@ -50,35 +50,64 @@ --size; thefile->ungetchar = -1; } - while (rv == 0 && size > 0) { - if (thefile->bufpos >= thefile->dataRead) { - int bytesread = read(thefile->filedes, thefile->buffer, - thefile->bufsize); - if (bytesread == 0) { - thefile->eof_hit = TRUE; - rv = APR_EOF; - break; - } - else if (bytesread == -1) { - rv = errno; - break; - } - thefile->dataRead = bytesread; - thefile->filePtr += thefile->dataRead; - thefile->bufpos = 0; + + if (size > thefile->bufsize) { + int bytesread; + + blocksize = thefile->dataRead - thefile->bufpos; + if (blocksize > 0) { + memcpy(pos, thefile->buffer + thefile->bufpos, blocksize); + thefile->bufpos += blocksize; + pos += blocksize; + size -= blocksize; } - blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size; - memcpy(pos, thefile->buffer + thefile->bufpos, blocksize); - thefile->bufpos += blocksize; - pos += blocksize; - size -= blocksize; + bytesread = read(thefile->filedes, pos, size); + if (bytesread == 0) { + thefile->eof_hit = TRUE; + rv = APR_EOF; + } + else if (bytesread == -1) { + rv = errno; + bytesread = 0; + } + + thefile->filePtr += bytesread; + pos += bytesread; } + else { + while (rv == 0 && size > 0) { + if (thefile->bufpos >= thefile->dataRead) { + int bytesread = read(thefile->filedes, thefile->buffer, + thefile->bufsize); + if (bytesread == 0) { + thefile->eof_hit = TRUE; + rv = APR_EOF; + break; + } + else if (bytesread == -1) { + rv = errno; + break; + } + thefile->dataRead = bytesread; + thefile->filePtr += thefile->dataRead; + thefile->bufpos = 0; + } + blocksize = size > thefile->dataRead - thefile->bufpos + ? thefile->dataRead - thefile->bufpos + : size; + memcpy(pos, thefile->buffer + thefile->bufpos, blocksize); + thefile->bufpos += blocksize; + pos += blocksize; + size -= blocksize; + } + } + *nbytes = pos - (char *)buf; - if (*nbytes) { - rv = 0; - } + if (*nbytes) + rv = APR_SUCCESS; + return rv; } @@ -149,10 +178,25 @@ apr_size_t rv; if (thefile->buffered) { - char *pos = (char *)buf; + const char *pos = (const char *)buf; int blocksize; int size = *nbytes; + if (size < 16 && + !(thefile->flags & APR_XTHREAD) && + thefile->direction == 1 && + thefile->bufpos + size <= thefile->bufsize) { + + char *dest = thefile->buffer + thefile->bufpos; + const char *end = (const char *)buf + size; + thefile->bufpos += size; + + for (; pos != end; ++pos, ++dest) + *dest = *pos; + + return APR_SUCCESS; + } + file_lock(thefile); if ( thefile->direction == 0 ) { @@ -167,18 +211,40 @@ } rv = 0; - while (rv == 0 && size > 0) { - if (thefile->bufpos == thefile->bufsize) /* write buffer is full*/ - rv = apr_file_flush_locked(thefile); - blocksize = size > thefile->bufsize - thefile->bufpos ? - thefile->bufsize - thefile->bufpos : size; - memcpy(thefile->buffer + thefile->bufpos, pos, blocksize); - thefile->bufpos += blocksize; - pos += blocksize; - size -= blocksize; + /* Large chunks shall not be buffered. They would cause at least one + * cache flush just "for themselves". So, we can pass them directly + * to the OS instead of copying them around and poking the OS for + * every 4k of data in the buffer. */ + if (size > thefile->bufsize) { + rv = apr_file_flush_locked(thefile); + if (rv == APR_SUCCESS) { + apr_ssize_t written; + + do { + written = write(thefile->filedes, buf, size); + } while (written == -1 && errno == EINTR); + if (written == -1) + rv = errno; + else + thefile->filePtr += written; + } } + else { + while (rv == 0 && size > 0) { + if (thefile->bufpos == thefile->bufsize) /* write buffer is full */ + rv = apr_file_flush_locked(thefile); + blocksize = size > thefile->bufsize - thefile->bufpos + ? thefile->bufsize - thefile->bufpos + : size; + memcpy(thefile->buffer + thefile->bufpos, pos, blocksize); + thefile->bufpos += blocksize; + pos += blocksize; + size -= blocksize; + } + } + file_unlock(thefile); return rv; @@ -283,9 +349,19 @@ APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile) { - apr_size_t nbytes = 1; + if (!(thefile->flags & APR_XTHREAD) && + thefile->buffered && + thefile->direction == 1 && + thefile->bufpos < thefile->bufsize) { - return apr_file_write(thefile, &ch, &nbytes); + thefile->buffer [thefile->bufpos++] = ch; + + return APR_SUCCESS; + } + else { + apr_size_t nbytes = 1; + return apr_file_write(thefile, &ch, &nbytes); + } } APR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile) @@ -296,9 +372,35 @@ APR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile) { - apr_size_t nbytes = 1; + apr_status_t rc; + apr_size_t bread; - return apr_file_read(thefile, ch, &nbytes); + if (thefile->ungetchar != -1) { + *ch = (char)thefile->ungetchar; + thefile->ungetchar = -1; + return APR_SUCCESS; + } + + if (!(thefile->flags & APR_XTHREAD) && + thefile->buffered && + thefile->direction == 0 && + thefile->bufpos < thefile->dataRead) { + *ch = thefile->buffer[thefile->bufpos++]; + return APR_SUCCESS; + } + + bread = 1; + rc = apr_file_read(thefile, ch, &bread); + + if (rc) { + return rc; + } + + if (bread == 0) { + thefile->eof_hit = TRUE; + return APR_EOF; + } + return APR_SUCCESS; } APR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile) Index: file_io/win32/readwrite.c =================================================================== --- file_io/win32/readwrite.c (revision 930006) +++ file_io/win32/readwrite.c (working copy) @@ -181,12 +181,14 @@ apr_size_t blocksize; apr_size_t size = *len; - apr_thread_mutex_lock(thefile->mutex); + if (thefile->flags & APR_XTHREAD) + apr_thread_mutex_lock(thefile->mutex); if (thefile->direction == 1) { rv = apr_file_flush(thefile); if (rv != APR_SUCCESS) { - apr_thread_mutex_unlock(thefile->mutex); + if (thefile->flags & APR_XTHREAD) + apr_thread_mutex_unlock(thefile->mutex); return rv; } thefile->bufpos = 0; @@ -195,35 +197,58 @@ } rv = 0; - while (rv == 0 && size > 0) { - if (thefile->bufpos >= thefile->dataRead) { - apr_size_t read; - rv = read_with_timeout(thefile, thefile->buffer, - thefile->bufsize, &read); - if (read == 0) { - if (rv == APR_EOF) - thefile->eof_hit = TRUE; - break; - } - else { - thefile->dataRead = read; - thefile->filePtr += thefile->dataRead; - thefile->bufpos = 0; - } + if (size > thefile->bufsize) { + apr_size_t read; + + blocksize = thefile->dataRead - thefile->bufpos; + if (blocksize > 0) { + memcpy(pos, thefile->buffer + thefile->bufpos, blocksize); + thefile->bufpos += blocksize; + pos += blocksize; + size -= blocksize; } - blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size; - memcpy(pos, thefile->buffer + thefile->bufpos, blocksize); - thefile->bufpos += blocksize; - pos += blocksize; - size -= blocksize; + rv = read_with_timeout(thefile, pos, size, &read); + if (read == 0 && rv == APR_EOF) + thefile->eof_hit = TRUE; + + thefile->filePtr += read; + pos += read; } + else { + while (rv == 0 && size > 0) { + if (thefile->bufpos >= thefile->dataRead) { + apr_size_t read; + rv = read_with_timeout(thefile, thefile->buffer, + thefile->bufsize, &read); + if (read == 0) { + if (rv == APR_EOF) + thefile->eof_hit = TRUE; + break; + } + else { + thefile->dataRead = read; + thefile->filePtr += thefile->dataRead; + thefile->bufpos = 0; + } + } + blocksize = size > thefile->dataRead - thefile->bufpos + ? thefile->dataRead - thefile->bufpos + : size; + memcpy(pos, thefile->buffer + thefile->bufpos, blocksize); + thefile->bufpos += blocksize; + pos += blocksize; + size -= blocksize; + } + } + *len = pos - (char *)buf; if (*len) { rv = APR_SUCCESS; } - apr_thread_mutex_unlock(thefile->mutex); + if (thefile->flags & APR_XTHREAD) + apr_thread_mutex_unlock(thefile->mutex); } else { /* Unbuffered i/o */ apr_size_t nbytes; @@ -236,32 +261,66 @@ return rv; } +static apr_status_t apr_file_write_locked(apr_file_t *thefile, const void *buf, apr_size_t nbytes) +{ + DWORD numbytes, written = 0; + apr_status_t rc = APR_SUCCESS; + const char *buffer = buf; + + do { + if (nbytes > APR_DWORD_MAX) { + numbytes = APR_DWORD_MAX; + } + else { + numbytes = (DWORD)nbytes; + } + + if (!WriteFile(thefile->filehand, buffer, numbytes, &written, NULL)) { + rc = apr_get_os_error(); + thefile->filePtr += written; + break; + } + + thefile->filePtr += written; + nbytes -= written; + buffer += written; + + } while (nbytes > 0); + + return rc; +} + + APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes) { apr_status_t rv; DWORD bwrote; + apr_size_t size = *nbytes; - /* If the file is open for xthread support, allocate and - * initialize the overlapped and io completion event (hEvent). - * Threads should NOT share an apr_file_t or its hEvent. + /* Our buffered I/O implementation does not use overlapped I/O. */ - if ((thefile->flags & APR_XTHREAD) && !thefile->pOverlapped ) { - thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool, - sizeof(OVERLAPPED)); - thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!thefile->pOverlapped->hEvent) { - rv = apr_get_os_error(); - return rv; - } - } - if (thefile->buffered) { char *pos = (char *)buf; apr_size_t blocksize; - apr_size_t size = *nbytes; - apr_thread_mutex_lock(thefile->mutex); + if (size < 16 && + !(thefile->flags & APR_XTHREAD) && + thefile->direction == 1 && + thefile->bufpos + size <= thefile->bufsize) { + char *dest = thefile->buffer + thefile->bufpos; + const char *end = pos + size; + + thefile->bufpos += size; + for (; pos != end; ++pos, ++dest) + *dest = *pos; + + return APR_SUCCESS; + } + + if (thefile->flags & APR_XTHREAD) + apr_thread_mutex_lock(thefile->mutex); + if (thefile->direction == 0) { // Position file pointer for writing at the offset we are logically reading from apr_off_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos; @@ -274,107 +333,141 @@ } rv = 0; - while (rv == 0 && size > 0) { - if (thefile->bufpos == thefile->bufsize) // write buffer is full - rv = apr_file_flush(thefile); - blocksize = size > thefile->bufsize - thefile->bufpos ? - thefile->bufsize - thefile->bufpos : size; - memcpy(thefile->buffer + thefile->bufpos, pos, blocksize); - thefile->bufpos += blocksize; - pos += blocksize; - size -= blocksize; + /* Large chunks shall not be buffered. They would cause at least one + * cache flush just "for themselves". So, we can pass them directly + * to the OS instead of copying them around and poking the OS for + * every 4k of data in the buffer. */ + if (size > thefile->bufsize) { + rv = apr_file_flush(thefile); + if (rv == APR_SUCCESS) + apr_file_write_locked(thefile, buf, size); } + else { + while (rv == 0 && size > 0) { + if (thefile->bufpos == thefile->bufsize) // write buffer is full + rv = apr_file_flush(thefile); - apr_thread_mutex_unlock(thefile->mutex); + blocksize = size > thefile->bufsize - thefile->bufpos ? + thefile->bufsize - thefile->bufpos : size; + memcpy(thefile->buffer + thefile->bufpos, pos, blocksize); + thefile->bufpos += blocksize; + pos += blocksize; + size -= blocksize; + } + } + + if (thefile->flags & APR_XTHREAD) + apr_thread_mutex_unlock(thefile->mutex); + return rv; - } else { - if (!thefile->pipe) { - apr_off_t offset = 0; - apr_status_t rc; - if (thefile->append) { - /* apr_file_lock will mutex the file across processes. - * The call to apr_thread_mutex_lock is added to avoid - * a race condition between LockFile and WriteFile - * that occasionally leads to deadlocked threads. - */ + } + + /* If the file is open for xthread support, allocate and + * initialize the overlapped and io completion event (hEvent). + * Threads should NOT share an apr_file_t or its hEvent. + */ + if ((thefile->flags & APR_XTHREAD) && !thefile->pOverlapped ) { + thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool, + sizeof(OVERLAPPED)); + thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!thefile->pOverlapped->hEvent) { + rv = apr_get_os_error(); + return rv; + } + } + + if (!thefile->pipe) { + apr_off_t offset = 0; + apr_status_t rc; + if (thefile->append) { + /* apr_file_lock will mutex the file across processes. + * The call to apr_thread_mutex_lock is added to avoid + * a race condition between LockFile and WriteFile + * that occasionally leads to deadlocked threads. + */ + if (thefile->flags & APR_XTHREAD) apr_thread_mutex_lock(thefile->mutex); - rc = apr_file_lock(thefile, APR_FLOCK_EXCLUSIVE); - if (rc != APR_SUCCESS) { + + rc = apr_file_lock(thefile, APR_FLOCK_EXCLUSIVE); + if (rc != APR_SUCCESS) { + if (thefile->flags & APR_XTHREAD) apr_thread_mutex_unlock(thefile->mutex); - return rc; - } - rc = apr_file_seek(thefile, APR_END, &offset); - if (rc != APR_SUCCESS) { + return rc; + } + rc = apr_file_seek(thefile, APR_END, &offset); + if (rc != APR_SUCCESS) { + if (thefile->flags & APR_XTHREAD) apr_thread_mutex_unlock(thefile->mutex); - return rc; - } + return rc; } - if (thefile->pOverlapped) { - thefile->pOverlapped->Offset = (DWORD)thefile->filePtr; - thefile->pOverlapped->OffsetHigh = (DWORD)(thefile->filePtr >> 32); - } - rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote, - thefile->pOverlapped); - if (thefile->append) { - apr_file_unlock(thefile); - apr_thread_mutex_unlock(thefile->mutex); - } } - else { - rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote, - thefile->pOverlapped); + if (thefile->pOverlapped) { + thefile->pOverlapped->Offset = (DWORD)thefile->filePtr; + thefile->pOverlapped->OffsetHigh = (DWORD)(thefile->filePtr >> 32); } - if (rv) { - *nbytes = bwrote; - rv = APR_SUCCESS; + rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote, + thefile->pOverlapped); + if (thefile->append) { + apr_file_unlock(thefile); + if (thefile->flags & APR_XTHREAD) + apr_thread_mutex_unlock(thefile->mutex); } - else { - (*nbytes) = 0; - rv = apr_get_os_error(); + } + else { + rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote, + thefile->pOverlapped); + } + if (rv) { + *nbytes = bwrote; + rv = APR_SUCCESS; + } + else { + (*nbytes) = 0; + rv = apr_get_os_error(); - /* XXX: This must be corrected, per the apr_file_read logic!!! */ - if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) { - - DWORD timeout_ms; + /* XXX: This must be corrected, per the apr_file_read logic!!! */ + if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) { - if (thefile->timeout == 0) { - timeout_ms = 0; - } - else if (thefile->timeout < 0) { - timeout_ms = INFINITE; - } - else { - timeout_ms = (DWORD)(thefile->timeout / 1000); - } - - rv = WaitForSingleObject(thefile->pOverlapped->hEvent, timeout_ms); - switch (rv) { - case WAIT_OBJECT_0: - GetOverlappedResult(thefile->filehand, thefile->pOverlapped, - &bwrote, TRUE); - *nbytes = bwrote; - rv = APR_SUCCESS; - break; - case WAIT_TIMEOUT: - rv = (timeout_ms == 0) ? APR_EAGAIN : APR_TIMEUP; - break; - case WAIT_FAILED: - rv = apr_get_os_error(); - break; - default: - break; - } - if (rv != APR_SUCCESS) { - if (apr_os_level >= APR_WIN_98) - CancelIo(thefile->filehand); - } + DWORD timeout_ms; + + if (thefile->timeout == 0) { + timeout_ms = 0; } + else if (thefile->timeout < 0) { + timeout_ms = INFINITE; + } + else { + timeout_ms = (DWORD)(thefile->timeout / 1000); + } + + rv = WaitForSingleObject(thefile->pOverlapped->hEvent, timeout_ms); + switch (rv) { + case WAIT_OBJECT_0: + GetOverlappedResult(thefile->filehand, thefile->pOverlapped, + &bwrote, TRUE); + *nbytes = bwrote; + rv = APR_SUCCESS; + break; + case WAIT_TIMEOUT: + rv = (timeout_ms == 0) ? APR_EAGAIN : APR_TIMEUP; + break; + case WAIT_FAILED: + rv = apr_get_os_error(); + break; + default: + break; + } + if (rv != APR_SUCCESS) { + if (apr_os_level >= APR_WIN_98) + CancelIo(thefile->filehand); + } } - if (rv == APR_SUCCESS && thefile->pOverlapped && !thefile->pipe) { - thefile->filePtr += *nbytes; - } } + if (rv == APR_SUCCESS && thefile->pOverlapped && !thefile->pipe) { + thefile->filePtr += *nbytes; + } + return rv; } /* ToDo: Write for it anyway and test the oslevel! @@ -405,9 +498,19 @@ APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile) { - apr_size_t len = 1; + if (!(thefile->flags & APR_XTHREAD) && + thefile->buffered && + thefile->direction == 1 && + thefile->bufpos < thefile->bufsize) { - return apr_file_write(thefile, &ch, &len); + thefile->buffer [thefile->bufpos++] = ch; + + return APR_SUCCESS; + } + else { + apr_size_t len = 1; + return apr_file_write(thefile, &ch, &len); + } } APR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile) @@ -421,6 +524,20 @@ apr_status_t rc; apr_size_t bread; + if (thefile->ungetchar != -1) { + *ch = (char)thefile->ungetchar; + thefile->ungetchar = -1; + return APR_SUCCESS; + } + + if (!(thefile->flags & APR_XTHREAD) && + thefile->buffered && + thefile->direction == 0 && + thefile->bufpos < thefile->dataRead) { + *ch = thefile->buffer[thefile->bufpos++]; + return APR_SUCCESS; + } + bread = 1; rc = apr_file_read(thefile, ch, &bread); @@ -473,47 +590,15 @@ APR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile) { - if (thefile->buffered) { - DWORD numbytes, written = 0; - apr_status_t rc = 0; - char *buffer; - apr_size_t bytesleft; + apr_status_t rc = 0; - if (thefile->direction == 1 && thefile->bufpos) { - buffer = thefile->buffer; - bytesleft = thefile->bufpos; - - do { - if (bytesleft > APR_DWORD_MAX) { - numbytes = APR_DWORD_MAX; - } - else { - numbytes = (DWORD)bytesleft; - } - - if (!WriteFile(thefile->filehand, buffer, numbytes, &written, NULL)) { - rc = apr_get_os_error(); - thefile->filePtr += written; - break; - } - - thefile->filePtr += written; - bytesleft -= written; - buffer += written; - - } while (bytesleft > 0); - - if (rc == 0) - thefile->bufpos = 0; - } - - return rc; + if (thefile->buffered && thefile->direction == 1 && thefile->bufpos) { + rc = apr_file_write_locked(thefile, thefile->buffer, thefile->bufpos); + if (rc == 0) + thefile->bufpos = 0; } - /* There isn't anything to do if we aren't buffering the output - * so just return success. - */ - return APR_SUCCESS; + return rc; } struct apr_file_printf_data {